package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.PartyGroupCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.constant.StrConsts;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.entity.PartyGroup;
import com.arpa.ntocc.common.service.IPartyGroupConfigService;
import com.arpa.ntocc.common.service.IPartyGroupService;
import com.arpa.ntocc.common.service.authorize.AuthorizeTokenService;
import com.arpa.oms.service.IOmsOutboundService;
import com.arpa.wms.cache.*;
import com.arpa.wms.common.util.ArpaCloudUtil;
import com.arpa.wms.common.util.LogisticsUtils;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.consts.*;
import com.arpa.wms.domain.dto.*;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.*;
import com.arpa.wms.domain.vo.*;
import com.arpa.wms.mapper.OutboundMapper;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.taobao.api.ApiException;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.CommonConst.LOT_NUMBER;
import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;

/**
 * <p>
 * 出库单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-08-07
 */
@Service
@Log4j2
public class OutboundServiceImpl extends ServiceImpl<OutboundMapper, Outbound> implements IOutboundService {

    @Autowired
    private ShipmentCache shipmentCache;
    private final IOutboundItemService iOutboundItemService;

    private final IDeliveryService iDeliveryService;
    private final IDeliveryItemService iDeliveryItemService;

    private final IInventoryService iInventoryService;

    private final IExpenseDetailService iExpenseDetailService;
    private final IOmsOutboundService omsOutboundService;
    @Autowired
    private ISupplierService supplierService;

    @Autowired
    private ArpaCloudUtil arpaCloudUtil;

    @Autowired
    private PartyCache partyCache;

    @Autowired
    private WarehouseCache warehouseCache;

    @Autowired
    private IPartyGroupService partyGroupService;

    @Autowired
    private LocationCache locationCache;

    @Autowired
    private UnitCache unitCache;

    @Autowired
    private GoodsUnitConversionCache goodsUnitConversionCache;

    @Autowired
    private IDeliveryDistributionService iDeliveryDistributionService;

    @Autowired
    private ICollectService iCollectService;

    @Autowired
    private IInventoryRecordService iInventoryRecordService;

    @Autowired
    private IOutboundRegisterService iOutboundRegisterService;

    @Autowired
    @Lazy
    private IWmsTaskService iWmsTaskService;

    @Autowired
    private IContainerService iContainerService;


    @Autowired
    private CustomerCache customerCache;
    @Autowired
    private PartyGroupCache partyGroupCache;
    @Autowired
    private GoodsCache goodsCache;
    @Autowired
    private BatchRuleCache batchRuleCache;
    @Autowired
    @Lazy
    private ConfirmDeliveryUpdateDeliveryStatusService confirmUpdateDeliveryStatusService;
    @Autowired
    @Lazy
    private IPickingService pickingService;
    @Autowired
    @Lazy
    private OutCheckService outCheckService;

    @Autowired
    @Lazy
    private IDeliveryService deliveryService;


    @Autowired
    private ICarrierService carrierService;

    @Autowired
    private IGoodsPnService goodsPnService;

    public OutboundServiceImpl(IOutboundItemService iOutboundItemService,
                               IDeliveryService iDeliveryService, IDeliveryItemService iDeliveryItemService,
                               IInventoryService iInventoryService,
                               IExpenseDetailService iExpenseDetailService, IPartyGroupService iPartyGroupService, IOmsOutboundService omsOutboundService) {
        this.iOutboundItemService = iOutboundItemService;
        this.iDeliveryService = iDeliveryService;
        this.iDeliveryItemService = iDeliveryItemService;
        this.iInventoryService = iInventoryService;
        this.iExpenseDetailService = iExpenseDetailService;
        this.omsOutboundService = omsOutboundService;
    }

    /**
     * 保存
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(Outbound entity) {
        if (StringUtils.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.simpleUUID());
        }
        if (StringUtils.isBlank(entity.getCreatedBy())) {
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
        }
        return super.save(entity);
    }

    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Outbound entity) {
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        return baseMapper.update(entity, new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, entity.getCode()).eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 查询列表
     *
     * @param outboundDTO
     * @return
     */
    @Override
    public List<OutboundVO> queryList(OutboundDTO outboundDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(outboundDTO.getSortField())) {
            outboundDTO.setSortField(CommonUtil.camel2Underline(outboundDTO.getSortField()));
        }
        List<OutboundVO> outboundVOList = baseMapper.queryList(outboundDTO);
        // 翻译
        outboundVOList.stream().forEach(e -> {
            e.setTypeName(DeliveryTypeEnum.translate(e.getType()));
            e.setWarehouseName(warehouseCache.translate(e.getWarehouseCode()));
        });

        return outboundVOList;
    }


    /**
     * 查询合计,包含总数
     * @param outboundDTO
     * @return
     */
    @Override
    public OutboundVO queryListTotal(OutboundDTO outboundDTO){
        return this.baseMapper.queryListTotal(outboundDTO);
    }


    /**
     * 出库确认并处理取消订单
     * @author liuxr
     * @date 2021/11/25 14:49
     * @param outbound
     * @return com.arpa.core.web.Result
     */
    @Deprecated
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result outCheckAndHandelCancelDelivery(Outbound outbound) {
        log.info("出库单出库确认，出库单：{}",JSONUtil.toJsonStr(outbound));
        String  deliverCode = outbound.getDeliveryCode();
        String groupCode = outbound.getGroupCode();
        log.info("出库单出库确认，处理订单是否取消。订单号:{}",deliverCode);
        List<String> deliverCodes = new ArrayList<>();
        deliverCodes.add(deliverCode);

        //订单来源是奇门，并且订单缺货，禁止出库
        Boolean isCanOutbound = validateIsCanOutbound(deliverCode,groupCode);
        if (!isCanOutbound) {
            return Result.error("订单：" + deliverCode + "已执行缺货操作，无法出库");
        }
        log.info("出库单出库确认，如果是奇门订单 确认");
        confirmDeliveryForQimenByDeliverCode(deliverCode,groupCode);

        Result result = outCheck(outbound);
        //更新订单状态为出库完成
        iDeliveryService.update(new LambdaUpdateWrapper<Delivery>()
                .eq(Delivery::getCode, deliverCode)
                .eq(Delivery::getGroupCode,groupCode)
                .set(Delivery::getOutboundCompletionTime, LocalDateTime.now())
                .set(Delivery::getStatus, DeliveryStatusEnum.DELIVERIED.getValue()));

        //只有销售出库扣费
        if (CommonConst.STR_ONE.equals(outbound.getType())) {
            outCheckService.deductionByBill(outbound);
        }

        return result;
    }

    private Boolean validateIsCanOutbound(String deliveryCode,String groupCode) {
        Boolean isCanOutbound = true;
        Delivery delivery = this.iDeliveryService.getOne(new LambdaQueryWrapper<Delivery>()
                .select(Delivery::getCode,Delivery::getIsDeliveringIfStockout)
                .eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode,groupCode)
                //(total_num - distribution_num)代表拣货缺货数量
                .apply("total_num - distribution_num + recheck_refund_quantity > 0 "));
        if (null != delivery && delivery.getIsDeliveringIfStockout() == 0) {
            isCanOutbound = false;
        }
        return isCanOutbound;
    }
    /**
     * 出库确认
     *
     * @param outbound
     * @return
     */
    @Deprecated
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result outCheck(Outbound outbound) {
        String groupCode = outbound.getGroupCode();
        // 更新明细的出库数量 出库数量 = 复核数量
        iOutboundItemService.update(new UpdateWrapper<OutboundItem>().lambda().eq(OutboundItem::getOutboundCode, outbound.getCode())
                .eq(OutboundItem::getGroupCode,groupCode)
                .eq(OutboundItem::getDeleted, 0)
                .setSql(" outbound_quantity = recheck_quantity")
                .setSql(" outbound_unit_quantity = recheck_unit_quantity"));

        List<OutboundItem> outboundItems = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda()
                .eq(OutboundItem::getOutboundCode, outbound.getCode())
                .eq(OutboundItem::getGroupCode,groupCode)
                .eq(OutboundItem::getDeleted, 0)
        );

        List<String> containerBarCodes = new ArrayList<>();
        List<String> invContainerBarCodes = new ArrayList<>();

        // 入库的商品code集合
        Map<String, Boolean> goodsCodes = new HashMap<>();

        // 非基本单位出库数量
        BigDecimal outboundUnitQuantity;
        // 总出库数量
        BigDecimal outSum = BigDecimal.ZERO;
        BigDecimal outUnitSum = BigDecimal.ZERO;
        // 总出库板数
        for (OutboundItem item : outboundItems) {
            // 更新临时库存（扣除）
            updateTempInventory(item);

            // 更新oms不可用库存数量
            iInventoryService.updateOmsContUseQuantity(outbound.getWarehouseCode(), outbound.getShipmentCode(), item.getGoodsCode());

            outboundUnitQuantity = item.getRecheckQuantity().divide(item.getBasicQuantity(),6, BigDecimal.ROUND_HALF_UP);

            // 回填订单明细、分配单的出库数量
            iDeliveryItemService.update(new UpdateWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getCode,item.getDeliveryItemCode()).eq(DeliveryItem::getGroupCode,groupCode)
                    .setSql(" outbound_quantity = outbound_quantity + " + item.getRecheckQuantity())
                    .setSql(" outbound_unit_quantity = outbound_unit_quantity + " + outboundUnitQuantity));
            iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                    .eq(DeliveryDistribution::getCode, item.getDeliveryDistributionCode())
                    .setSql(" outbound_quantity = outbound_quantity + " + item.getRecheckQuantity()));

            outSum = outSum.add(item.getRecheckQuantity());
            outUnitSum = outUnitSum.add(item.getRecheckUnitQuantity());

            if (StrUtil.isNotBlank(item.getContainerBarCode())) {
                containerBarCodes.add(item.getContainerBarCode());
            }
            if (StrUtil.isNotBlank(item.getInventoryContainerBarCode())) {
                invContainerBarCodes.add(item.getInventoryContainerBarCode());
            }

            goodsCodes.put(item.getGoodsCode(), true);

            // TODO: 2021/7/23  出库单明细生成作业费
//            generateOperationCost(outbound, item);
        }

        // 释放容器（库存上的容器、拣货时使用的容器）  需要与上面的遍历分开，避免查询库存时出现偏差
        // item 上containerBarCode一定可以释放，这是拣货时使用的，出库后一定不会使用了，可以一次性释放
//        iContainerService.batchReleaseContainer(containerBarCodes);
        // item 上inventoryContainerBarCode需要判断库存上是否还有使用的
        iContainerService.batchReleaseContainerForEmptyInventory(invContainerBarCodes,groupCode);
        //尝试释放波次绑定的容器
        pickingService.releaseContainer(outbound.getWavePickingCode(),groupCode);

        // 更新出库单状态、出库数量
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                .eq(Outbound::getGroupCode,groupCode)
                .set(Outbound::getStatus, OutboundStatusEnum.FINISH_OUT.getValue())
                .set(Outbound::getOutQuantity, outSum)
                .set(Outbound::getOutUnitQuantity, outUnitSum));

        // 回填订单表的出库数量
        DeliveryDTO deliveryDTO = new DeliveryDTO();
        deliveryDTO.setCode(outbound.getDeliveryCode());
        deliveryDTO.setSendNum(outSum);
        deliveryDTO.setGroupCode(groupCode);
        iDeliveryService.updateSendNum(deliveryDTO);

        // 获取最新的出库信息
        outbound = getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode()).eq(Outbound::getGroupCode,groupCode));

        // 发货单后续操作，如更新oms单据，更新领用、破损、销售退单信息等
        iDeliveryService.afterOutbound(outbound, outboundItems);

        // 使用oms 回写数据
        omsOutboundService.saveByWmsOutbound(outbound, outboundItems);

        return Result.ok("出库确认成功");
    }

    /**
     * 回调奇门确认订单
     *
     * @param deliveryCode 订单号
     * @author liuxr
     * @date 2021/11/25 11:00
     */
    private void confirmDeliveryForQimenByDeliverCode(String deliveryCode,String groupCode) {
        Delivery delivery = iDeliveryService.getOne(Wrappers.lambdaQuery(Delivery.class)
                .select(Delivery::getCode,
                        Delivery::getWaybillCode,
                        Delivery::getSourceType,
                        Delivery::getCustomerId,
                        Delivery::getWarehouseSerialNumber,
                        Delivery::getOrderType,
                        Delivery::getLogisticsCode,
                        Delivery::getCarrierType,
                        Delivery::getDeliveryOrderCode,
                        Delivery::getGroupCode)
                .eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode,groupCode)
                .eq(Delivery::getSourceType, "qimen"));
        if (delivery == null) {
            log.info("订单：{},为非奇门订单，无需奇门确认", deliveryCode);
            return;
        }
        //校验来自奇门的订单是否有面单号
        if (StrUtil.isEmpty(delivery.getWaybillCode())) {
            log.info("订单:{}没有面单号，无法回调奇门订单确认接口。",deliveryCode);
            throw new ServiceException("订单:" + deliveryCode + "没有面单号，无法回调奇门订单确认接口，请确认");
        }
        //判断如果是奇门订单，需要奇门确认
        try {
            List<DeliveryItem> deliveryItemLists = this.iDeliveryItemService.list(new LambdaQueryWrapper<DeliveryItem>()
                    .select(DeliveryItem::getGoodCode,
                            DeliveryItem::getPlanQuantity,
                            DeliveryItem::getActualPrice,
                            DeliveryItem::getSourceOrderCode,
                            DeliveryItem::getCode)
                    .eq(DeliveryItem::getGroupCode,groupCode)
                    .eq(DeliveryItem::getDeliveryCode, deliveryCode));
            //调拨出库 和  采购退货需要调用出库单确认接口  。  发货调用发货单创建接口
            if(StrUtil.equalsAny(delivery.getOrderType(),"DBCK","CGTH")){
                confirmUpdateDeliveryStatusService.stokcoutorderConfirm(delivery, deliveryItemLists);
            }else{
                confirmUpdateDeliveryStatusService.deliveryorderConfirm(delivery, deliveryItemLists);
            }
        } catch (ApiException e) {
            log.error("出库失败，订单确认失败:：", e);
            throw new ServiceException(e);
        }
    }

    private void handleInvalid(Outbound outbound) {
        if (StrUtil.isBlank(outbound.getTemporaryCode())) {
            //按单出库处理作废单
            handleSingleInvalid(outbound);
        } else {
            //批量出库处理作废单
            handleBatchInvalid(outbound);
        }
    }

    private void handleSingleInvalid(Outbound outbound) {
        Delivery delivery = iDeliveryService.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, outbound.getDeliveryCode())
                .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                .ne(Delivery::getStatus, DeliveryStatusEnum.VOID.getValue())
                .eq(Delivery::getDeleted, NumConst.NUM_ZERO).eq(Delivery::getIsCancel, NUM_ONE));
        if (ObjectUtil.isNotNull(delivery)) {
            //释放库存到退拣库位
            List<OutboundItem> outboundItems = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda()
                    .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                    .eq(OutboundItem::getOutboundCode, outbound.getCode()).eq(OutboundItem::getDeleted, NumConst.NUM_ZERO));
            for (OutboundItem item : outboundItems) {
                iInventoryService.stockoutInventory(item.getInventoryCode(), item.getPlanQuantity(), StockoutTypeEnum.OUT_CHECK.getValue(), item.getOutboundCode());
            }
            //含有作废单时，将出库单、出库明细、出库确认的拣货数量、退拣数量、复核数量置零
            this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode()).set(Outbound::getPlanQuantity, BigDecimal.ZERO)
                    .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                    .set(Outbound::getRecheckQuantity, BigDecimal.ZERO).set(Outbound::getReturnQuantity, BigDecimal.ZERO));
            iOutboundItemService.update(new UpdateWrapper<OutboundItem>().lambda().eq(OutboundItem::getOutboundCode, outbound.getCode())
                    .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                    .set(OutboundItem::getPlanQuantity, BigDecimal.ZERO).set(OutboundItem::getRecheckQuantity, BigDecimal.ZERO)
                    .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                    .set(OutboundItem::getRecheckUnitQuantity, BigDecimal.ZERO).set(OutboundItem::getReturnQuantity, BigDecimal.ZERO));
            iOutboundRegisterService.update(new UpdateWrapper<OutboundRegister>().lambda().eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode()).eq(OutboundRegister::getOutboundCode, outbound.getCode())
                    .set(OutboundRegister::getPlanQuantity, BigDecimal.ZERO).set(OutboundRegister::getRecheckQuantity, BigDecimal.ZERO)
                    .set(OutboundRegister::getNotRecheckQuantity, BigDecimal.ZERO));
        }
    }

    private void handleBatchInvalid(Outbound outbound) {
        List<Delivery> deliveryList = iDeliveryService.list(new QueryWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, outbound.getWavePickingCode())
                .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                .ne(Delivery::getStatus, DeliveryStatusEnum.VOID.getValue())
                .eq(Delivery::getDeleted, NumConst.NUM_ZERO).eq(Delivery::getIsCancel, NUM_ONE));
        //作废发货单总拣货数量
        BigDecimal totalPickingQuantity = BigDecimal.ZERO;
        //作废发货单总退拣数量
        BigDecimal totalRecheckRefundQuantity = BigDecimal.ZERO;
        //作废发货单总复核数量
        BigDecimal totalRecheckQuantity = BigDecimal.ZERO;
        //汇总作废单的总拣货数量、总退拣数量、总复核数量
        if (IterUtil.isNotEmpty(deliveryList)) {
            for (Delivery delivery : deliveryList) {
                List<DeliveryDistribution> distributions = iDeliveryDistributionService.list(new QueryWrapper<DeliveryDistribution>().lambda()
                        .eq(DeliveryDistribution::getDeliveryCode, delivery.getCode()).eq(DeliveryDistribution::getDeleted, NumConst.NUM_ZERO));
                Map<String, List<DeliveryDistribution>> listMap = distributions.stream().collect(Collectors.groupingBy(DeliveryDistribution::getInventoryCode));
                for (Map.Entry<String, List<DeliveryDistribution>> entry : listMap.entrySet()) {
                    BigDecimal pickingQuantity = entry.getValue().stream().map(DeliveryDistribution::getPickingQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal recheckRefundQuantity = entry.getValue().stream().map(DeliveryDistribution::getRecheckRefundQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal recheckQuantity = pickingQuantity.subtract(recheckRefundQuantity);
                    iInventoryService.stockoutInventory(entry.getKey(), pickingQuantity, StockoutTypeEnum.OUT_CHECK.getValue(), outbound.getCode());
                    //更新出库明细、出库确认单信息
                    iOutboundItemService.update(new UpdateWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,UserUtil.getBranchCode()).eq(OutboundItem::getWavePickingCode, outbound.getWavePickingCode()).eq(OutboundItem::getInventoryCode, entry.getKey())
                            .setSql(SqlConst.PLAN_QUANTITY_EQUALS_PLAN_QUANTITY + pickingQuantity.negate()).setSql(SqlConst.RECHECK_QUANTITY_EQUALS_RECHECK_QUANTITY + recheckQuantity.negate())
                            .setSql("recheck_unit_quantity=recheck_unit_quantity+" + recheckQuantity.negate()).setSql("recheck_refund_quantity=recheck_refund_quantity+" + recheckRefundQuantity.negate()));
                    iOutboundRegisterService.update(new UpdateWrapper<OutboundRegister>().lambda().eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode()).eq(OutboundRegister::getWavePickingCode, outbound.getWavePickingCode()).eq(OutboundRegister::getInventoryCode, entry.getKey())
                            .setSql(SqlConst.PLAN_QUANTITY_EQUALS_PLAN_QUANTITY + pickingQuantity.negate()).setSql(SqlConst.RECHECK_QUANTITY_EQUALS_RECHECK_QUANTITY + recheckQuantity.negate()));

                    totalPickingQuantity = totalPickingQuantity.add(pickingQuantity);
                    totalRecheckRefundQuantity = totalRecheckRefundQuantity.add(recheckRefundQuantity);
                    totalRecheckQuantity = totalRecheckQuantity.add(recheckQuantity);
                }

            }
        }
        //更新出库单、出库明细单、出库确认单出的拣货数量、复核数量等
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode()).setSql(SqlConst.PLAN_QUANTITY_EQUALS_PLAN_QUANTITY + totalPickingQuantity.negate())
                .setSql(SqlConst.RECHECK_QUANTITY_EQUALS_RECHECK_QUANTITY + totalRecheckQuantity.negate()).setSql("recheck_refund_quantity=recheck_refund_quantity+" + totalRecheckRefundQuantity.negate()));

        // 更新订单的状态，改为作废
        iDeliveryService.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, outbound.getWavePickingCode())
                .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                .ne(Delivery::getStatus, DeliveryStatusEnum.VOID.getValue())
                .eq(Delivery::getDeleted, NumConst.NUM_ZERO).eq(Delivery::getIsCancel, NUM_ONE)
                .set(Delivery::getStatus, DeliveryStatusEnum.VOID.getValue()));
    }

    /**
     * @param outbound:
     * @param item:
     * @description 出库单明细生成作业费
     * @author xuyang
     * @date 2020/12/28 13:13
     **/
    @Transactional(rollbackFor = {Exception.class})
    public void generateOperationCost(Outbound outbound, OutboundItem item) {
        // 出库生成作业费
        OperationCostSourceDataDTO sourceDataDTO = new OperationCostSourceDataDTO();
        BeanUtils.copyProperties(outbound, sourceDataDTO);
        sourceDataDTO.setSourceCode(outbound.getCode());
        sourceDataDTO.setType(OrderIdGeneratorUtil.OrderTypeEnum.CK);
        sourceDataDTO.setGoodsCode(item.getGoodsCode());
        sourceDataDTO.setGoodCode(item.getGoodCode());
        sourceDataDTO.setGoodsName(item.getGoodsName());
        sourceDataDTO.setQuantity(item.getOutboundQuantity());
        sourceDataDTO.setReceiveLot(item.getReceiveLot());
        // 出库单明细已经打散成基本单位了，那总重量是根据基本单位计算？
        BigDecimal basicUnitWeight = goodsUnitConversionCache.getBasicUnitWeight(item.getGoodsCode());
        // 明细总重量 = 基本单位下定义的单个重量 * 发货明细数量
        sourceDataDTO.setWeight(item.getOutboundQuantity().multiply(basicUnitWeight));

        // 出库单明细对应的库位应该只会有一个
        sourceDataDTO.setLocationCount(BigDecimal.ONE);
        sourceDataDTO.setGmtOrderCreated(outbound.getGmtCreated().toLocalDate());
        iExpenseDetailService.generateOperationCost(sourceDataDTO);
    }

    /**
     * 更新临时库存
     *
     * @param outboundItem
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateTempInventory(OutboundItem outboundItem) {
        String inventoryCode = outboundItem.getInventoryCode();
        BigDecimal changeQuantity = outboundItem.getRecheckQuantity().negate();
        iInventoryService.adjustQuantity(inventoryCode, changeQuantity);

        // 记录库存变动日志
        Inventory tempInventory = iInventoryService.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, inventoryCode).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        iInventoryRecordService.insertRecord(tempInventory, outboundItem.getOutboundCode(), changeQuantity, InventoryRecordTypeEnum.OUTBOUND.getValue());
    }

    @Override
    public OutboundVO getVOByCode(String code) {
        Outbound entity = getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, code).eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        OutboundVO vo = new OutboundVO();
        BeanUtil.copyProperties(entity, vo);
        // 翻译
        vo.setWarehouseName(warehouseCache.translate(vo.getWarehouseCode()));
        vo.setShipmentName(shipmentCache.translate(vo.getShipmentCode()));
        vo.setTypeName(DeliveryTypeEnum.translate(vo.getType()));
        //　获取明细
        List<OutboundItem> items = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,UserUtil.getBranchCode()).eq(OutboundItem::getOutboundCode, code).eq(OutboundItem::getDeleted, 0));
        List<OutboundItemVO> itemVOS = new ArrayList<>();
        items.stream().forEach(e -> {
            OutboundItemVO itemVO = new OutboundItemVO();
            BeanUtil.copyProperties(e, itemVO);
            itemVO.setGoodsUnitName(unitCache.translate(itemVO.getGoodsUnit()));
            itemVO.setLocationName(locationCache.translate(itemVO.getLocation()));
            itemVOS.add(itemVO);
        });
        vo.setOutboundItemVOList(itemVOS);

        return vo;
    }

    @Override
    public OutboundVO getVOWithRegisterByCode(String code) {
        Outbound entity = getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, code).eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        OutboundVO vo = new OutboundVO();
        BeanUtil.copyProperties(entity, vo);
        // 翻译
        vo.setWarehouseName(warehouseCache.translate(vo.getWarehouseCode()));
        vo.setShipmentName(shipmentCache.translate(vo.getShipmentCode()));
        vo.setGroupName(partyGroupCache.translate(vo.getGroupCode()));
        vo.setCustomerName(customerCache.translate(vo.getCustomerCode()));
        vo.setTypeName(DeliveryTypeEnum.translate(vo.getType()));
        vo.setLogisticsName(LogisticsUtils.translate(vo.getLogisticsCode()));

        if (StrUtil.isNotBlank(entity.getDeliveryCode())) {
            Delivery delivery = iDeliveryService.getOne(new LambdaQueryWrapper<Delivery>()
                    .select(Delivery::getReceiverCity,
                            Delivery::getReceiverName,
                            Delivery::getReceiverProvince,
                            Delivery::getReceiverDetailAddress,
                            Delivery::getReceiverDistrict,
                            Delivery::getReceiverMobile)
                    .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                    .eq(Delivery::getCode, entity.getDeliveryCode()));
            if(ObjectUtil.isNotNull(delivery)){
                String supplierCode = delivery.getSupplierCode();
                if (StrUtil.isNotBlank(supplierCode)) {
                    Supplier supplier = supplierService.getOne(new LambdaQueryWrapper<Supplier>().select(Supplier::getName).eq(Supplier::getCode, supplierCode).eq(Supplier::getGroupCode,UserUtil.getBranchCode()));
                    if (supplier != null) {
                        vo.setSupplierName(supplier.getName());
                    }
                }
                vo.setReceiverCity(delivery.getReceiverCity());
                vo.setReceiverProvince(delivery.getReceiverProvince());
                vo.setReceiverDetailAddress(delivery.getReceiverDetailAddress());
                vo.setReceiverDistrict(delivery.getReceiverDistrict());
                vo.setReceiverMobile(delivery.getReceiverMobile());
                vo.setReceiverName(delivery.getReceiverName());
            }
        }
        //　获取明细关联确认单列表
        OutboundItemDTO outboundItemDTO = new OutboundItemDTO();
        outboundItemDTO.setOutboundCode(vo.getCode());
        List<OutboundItemWithRegisterVO> itemWithRegisterVOS = iOutboundItemService.queryItemWithRegisterList(outboundItemDTO);
        //如果复核数量为null.赋值为0
        for (OutboundItemWithRegisterVO outboundItemWithRegisterVO : itemWithRegisterVOS) {
            if (ObjectUtil.isNull(outboundItemWithRegisterVO.getRecheckQuantity())) {
                outboundItemWithRegisterVO.setRecheckQuantity(BigDecimal.ZERO);
            }
        }
        //过滤掉拣货数量为空的出库单明细以及复核数量为零的出库单
        itemWithRegisterVOS = itemWithRegisterVOS.stream().filter(e -> e.getPlanQuantity() != null && e.getPlanQuantity().compareTo(BigDecimal.ZERO) > NUM_ZERO).collect(Collectors.toList());
        itemWithRegisterVOS.stream().forEach(e -> {
            e.setLocationName(locationCache.translate(e.getLocation()));
            e.setStatusText(OutboundRecheckStatusEnum.translate(e.getStatus()));
        });
        vo.setOutboundItemWithRegisterVOList(itemWithRegisterVOS);

        return vo;
    }

    /**
     * 出库确认更新领用单信息
     * 该功能已被updateSourceQuantity 取代，可以删除
     *
     * @param outbound
     * @return
     */
    @Deprecated
    @Transactional(rollbackFor = {Exception.class})
    public Result updateCollect(Outbound outbound) {
        Collect collect = iCollectService.getOne(new QueryWrapper<Collect>().lambda().eq(Collect::getCode, outbound.getSourceCode()).eq(Collect::getGroupCode,UserUtil.getBranchCode()));
        iCollectService.update(new UpdateWrapper<Collect>().lambda().eq(Collect::getCode, collect.getCode())
                .eq(Collect::getGroupCode,UserUtil.getBranchCode())
                .set(Collect::getTotalActualQuantity, outbound.getOutQuantity()));

        return Result.ok();
    }


    /**
     * 更新出库单下发状态
     *
     * @param code 出库单标识
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateIssueStatus(String code) {
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, code)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .set(Outbound::getIssue, IssueStatusEnum.ISSUED));
    }

    /**
     * @param outboundVO :
     * @description PC端复核出库单
     * @author xuyang
     * @date 2020/10/28 8:15
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result PCRecheck(OutboundVO outboundVO) {
        String groupCode = UserUtil.getBranchCode();
        //高并发下调用工具类提前
        String operator = UserUtil.getCode();
        String operatorName = partyCache.translate(operator);
        outboundVO.setGroupCode(groupCode);
        Result actionRS = Result.ok();
        // 获取数据库中的出库单
        Outbound outbound = this.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, outboundVO.getCode()).eq(Outbound::getGroupCode,groupCode));
        //预打包单复核校验
        if (StrUtil.isNotEmpty(outbound.getPrepackagedCode())) {
            List<OutboundItemWithRegisterVO> recheckQuantityZeroList = outboundVO.getOutboundItemWithRegisterVOList().stream()
                    .filter(e -> BigDecimal.ZERO.compareTo(e.getRecheckQuantity()) == 0).collect(Collectors.toList());
            if (recheckQuantityZeroList.size() > 0) {
                throw new ServiceException("复核失败，预打包订单复核数量不能为0");
            }
        }
        String wavePickingCode = outbound.getWavePickingCode();
        // 判断出库单是否存在、状态是否正确
        if (!StrUtil.equalsAny(outbound.getStatus(), OutboundStatusEnum.NOT_RECHECK.getValue(), OutboundStatusEnum.RECHECKING.getValue())) {
            return Result.error("出库单已复核，不能重复复核");
        }

        String deliverCode = outbound.getDeliveryCode();
        if (StrUtil.isBlank(deliverCode)) {
            return Result.error("出库单复核失败，找不到对应订单");
        }
        String outboundCode = outbound.getCode();
        //log.info("出库单复核，先处理取消订单");
        //List<String> deliverCodes = new ArrayList<>();
        //deliverCodes.add(deliverCode);
        //Set<String> cancelCodes = orderCancelService.deliveryOrderCancel(deliverCodes);
        //
        //String outboundCode = outbound.getCode();
        //log.info("出库单复核，取消订单:{}", JSONUtil.toJsonStr(cancelCodes));
        //if (IterUtil.isNotEmpty(cancelCodes)) {
        //    update(Wrappers.lambdaUpdate(Outbound.class).eq(Outbound::getCode, outboundCode).set(Outbound::getStatus, OutboundStatusEnum.CANCEL_OUT.getValue()));
        //    return Result.error("订单：" + deliverCode + "已取消，出库单:" + outboundCode + "取消出库");
        //}



        // 删除待确认确认单
        iOutboundRegisterService.remove(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,groupCode)
                .eq(OutboundRegister::getOutboundCode, outboundVO.getCode()).eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.NOT_RECHECK));

        // 更新明细信息，如果复核数量有异，则会回滚的，缺货则会退减
        Map<OutboundItem, BigDecimal> itemsRecheckQuantity = calcRecheckQuantity(outboundVO, false);

        // 保存前端未复核状态的数据
        savePCUnSaveRegister(outboundVO);

        // 更新出库明细和订单明细、订单确认单的复核数量
        updateItemForRecheck(itemsRecheckQuantity);
        BigDecimal recheckQuantitySum = BigDecimal.ZERO;
        for (BigDecimal val : itemsRecheckQuantity.values()) {
            recheckQuantitySum = recheckQuantitySum.add(val);
        }

        // 更新出库主单状态、复核数量、退减数量
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outboundCode).eq(Outbound::getGroupCode, groupCode)
                .set(Outbound::getRecheckQuantity, recheckQuantitySum)
                .setSql(" recheck_refund_quantity = plan_quantity -" + recheckQuantitySum)
                .setSql(" return_quantity = recheck_refund_quantity ")
                .set(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));

        // 更新发货模块相关的分配数量
        updateDistributionQuantityByOut(outbound);
        //更新发货主单的复核数量
        iDeliveryService.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, deliverCode)
                .eq(Delivery::getGroupCode,groupCode)
                .set(Delivery::getRecheckQuantity, recheckQuantitySum)
                .setSql("recheck_refund_quantity = pick_num - recheck_quantity"));

        // 更新任务（如果有的话） 状态、作业数量
        BigDecimal jobQuantity = BigDecimal.ZERO;
        for (BigDecimal val : itemsRecheckQuantity.values()) {
            jobQuantity = jobQuantity.add(val);
        }
        // 更新任务操作
        actionRS = finishTask(outboundVO, jobQuantity,operator,operatorName);

        // 释放容器， 包括库存容器，拣货容器
//        this.outReleasePickContainer(outboundCode);
        this.outReleaseInvContainer(outboundCode,groupCode);
        //尝试释放波次绑定的容器
        pickingService.releaseContainer(outbound.getWavePickingCode(),groupCode);

        //将订单状态更新为”待出库“
        iDeliveryService.updateStatusByCode(outbound.getDeliveryCode(), DeliveryStatusEnum.UN_DELIVERY.getValue(),groupCode);

        //处理订单完全缺货的逻辑
        handleWholeStockOutDelivery(deliverCode, outboundCode,groupCode);

        try {
            //自动出库
            log.info("自动出库");
            outCheckService.autoOutConfirmation(wavePickingCode, deliverCode, NUM_ONE, NUM_ONE,groupCode);

//            log.info("异步释放容器");
//            outCheckService.releaseContainer(wavePickingCode);
//            pickingService.releaseContainer(wavePickingCode,groupCode);
        } catch (Exception e) {
            log.error("错误信息为：" + e.getMessage());
        }


        return actionRS;
    }


    /**
     * 出库复核时更新发货单分配数量
     *
     * @param outbound
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateDistributionQuantityByOut(Outbound outbound) {
        // 查询有缺货登记的数据，缺货登记才需要调整分配数量
        List<OutboundItem> stockOutItem = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda()
                .eq(OutboundItem::getGroupCode,outbound.getGroupCode())
                .eq(OutboundItem::getOutboundCode, outbound.getCode())
                .gt(OutboundItem::getReturnQuantity, BigDecimal.ZERO));
        updateDistributionQuantityForRecheck(outbound, stockOutItem);
    }

    /**
     * 出库复核时更新发货单分配数量
     *
     * @param item
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateDistributionQuantityByOutItem(Outbound outbound, OutboundItem item) {
        // 查询有缺货登记的数据，缺货登记才需要调整分配数量
        List<OutboundItem> stockOutItem = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda()
                .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundItem::getCode, item.getCode()));
        updateDistributionQuantityForRecheck(outbound, stockOutItem);
    }

    /**
     * 出库复核时更新发货单分配数量
     *
     * @param stockOutItem
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateDistributionQuantityForRecheck(Outbound outbound, List<OutboundItem> stockOutItem) {
        String groupCode = outbound.getGroupCode();
        if (IterUtil.isNotEmpty(stockOutItem)) {
            // 更新分配单中的分配数量
            stockOutItem.forEach(e -> {
                iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                        .eq(DeliveryDistribution::getGroupCode,groupCode)
                        .eq(DeliveryDistribution::getCode, e.getDeliveryDistributionCode())
                        .setSql(" distribution_quantity = distribution_quantity - " + e.getReturnQuantity()));
            });
            // 获取涉及到的分配单明细，只更新这部分分配单明细
            Map<String, List<OutboundItem>> deliveryItemCodes = stockOutItem.stream().collect(Collectors.groupingBy(OutboundItem::getDeliveryItemCode));
            iDeliveryItemService.updateDistributionQuantityForOutboundRecheck(deliveryItemCodes.keySet());
            // 更新对应发货单分配数量
            iDeliveryService.updateDistributionQuantityForOutboundRecheck(outbound.getDeliveryCode(),groupCode);
            // 更新发货单状态
            iDeliveryService.updateDeliveryStatusIfEmptyDistribution(outbound.getDeliveryCode(),groupCode);
        }
    }

    /**
     * @param dto :
     * @description PDA端复核
     * @author xuyang
     * @date 2020/10/28 17:17
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public PdaSwitchPageVO pdaSingleRecheck(OutboundRegisterDTO dto) {
        String groupCode = dto.getGroupCode();
        PdaSwitchPageVO switchPageVO = new PdaSwitchPageVO();
        // 默认PDA不跳转
        switchPageVO.setToPage(NumConst.NUM_ZERO);
        switchPageVO.setMsg("操作成功");

        OutboundRegister register = iOutboundRegisterService.getOne(new QueryWrapper<OutboundRegister>().lambda().eq(OutboundRegister::getGroupCode,groupCode).eq(OutboundRegister::getCode, dto.getCode()));
        if (ObjectUtil.isNull(register)) {
            throw new ServiceException("复核商品数据不存在");
        }

        // 校验复核数量，必须>0
        if (ObjectUtil.isNull(dto.getRecheckQuantity()) || BigDecimal.ZERO.compareTo(dto.getRecheckQuantity()) >= 0) {
            throw new ServiceException("复核数量必须大于0");
        }

        register.setRecheckQuantity(dto.getRecheckQuantity());

        WmsTask task = iWmsTaskService.getOne(new QueryWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, register.getOutboundCode()).eq(WmsTask::getGroupCode,groupCode));
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("任务信息不存在");
        }

        // 更新任务状态, 此时也会自动占有任务
        iWmsTaskService.updateTaskWorkingBySourceCode(register.getOutboundCode());

        // 检查主单状态
        Outbound outbound = this.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, register.getOutboundCode()).eq(Outbound::getGroupCode,groupCode));
        if (ObjectUtil.isNull(outbound)) {
            throw new ServiceException(TipConst.OUTBOUND_NOT_EXIST);
        }
        if (!(outbound.getStatus().equals(OutboundStatusEnum.NOT_RECHECK.getValue()) || outbound.getStatus().equals(OutboundStatusEnum.RECHECKING.getValue()))) {
            throw new ServiceException("出库单非待复核状态，无法复核确认");
        }
        String wavePickingCode = outbound.getWavePickingCode();
        // 计算出库单明细数量是否超出
        OutboundItem item = iOutboundItemService.getOne(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getCode, register.getOutboundItemCode()).eq(OutboundItem::getGroupCode,groupCode));
        BigDecimal diffQuantity = item.getPlanQuantity().subtract(item.getReturnQuantity()).subtract(register.getRecheckQuantity());
        if (diffQuantity.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("复核数量与缺货数量之和超过了该明细的总复核数量，无法进行复核确认");
        }
        // 判断是否有相同数据，然后保存
        register.setTaskCode(task.getCode());
        iOutboundRegisterService.distinctSave(register);

        // 计算出非基本单位下的复核数量
        BigDecimal recheckUnitQuantity = register.getRecheckQuantity().divide(item.getBasicQuantity(), 6, BigDecimal.ROUND_HALF_UP);

        //更新订单、订单明细，订单分配的复核数量(爆品、按单)
        updateDeliveryRecheckQuantity2(register, outbound.getDeliveryCode(),groupCode);
        // 更新明细下未复核信息的待复核量
        iOutboundRegisterService.update(new UpdateWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,groupCode)
                .eq(OutboundRegister::getOutboundItemCode, register.getOutboundItemCode()).eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.NOT_RECHECK)
//                .setSql(" recheck_quantity = recheck_quantity - " + register.getRecheckQuantity())
                .setSql(" not_recheck_quantity = not_recheck_quantity - " + register.getRecheckQuantity()));

        // 更新明细中的数量
        iOutboundItemService.update(new UpdateWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,groupCode)
                .eq(OutboundItem::getCode, register.getOutboundItemCode())
                .setSql(" recheck_quantity = recheck_quantity + " + register.getRecheckQuantity())
                .setSql(" recheck_unit_quantity = recheck_unit_quantity + " + recheckUnitQuantity));
        // 更新主表的复核量
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, register.getOutboundCode())
                .eq(Outbound::getGroupCode,groupCode)
                .setSql(" recheck_quantity = recheck_quantity + " + register.getRecheckQuantity()));

        // 更新任务作业量，作业状态在占用作业时就已更新，所以不需要再更新
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, outbound.getCode())
                .eq(WmsTask::getGroupCode,groupCode)
                .setSql(" job_quantity = job_quantity + " + register.getRecheckQuantity()));

        // 尝试更新明细完成状态
        int finishItemFlag = iOutboundItemService.updateStatusIfFinishRecheck(register.getOutboundItemCode(), OutboundRecheckStatusEnum.RECHECKED.getValue());
        if (finishItemFlag > 0) {
            // 明细复核完成，则可跳转到复核详情页面
            switchPageVO.setToPage(NUM_ONE);

            // 删除明细下待复核的数据
            iOutboundRegisterService.remove(new QueryWrapper<OutboundRegister>().lambda()
                    .eq(OutboundRegister::getGroupCode,groupCode)
                    .eq(OutboundRegister::getOutboundItemCode, register.getOutboundItemCode()).eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.NOT_RECHECK));
            // 尝试更新主单的状态
            int finishOutboundFlag = this.updateStatusIfFinishRecheck(register.getOutboundCode(), OutboundStatusEnum.NOT_OUT.getValue());

            if (finishOutboundFlag > 0) {
                //更新任务状态
                iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getCode, task.getCode())
                        .eq(WmsTask::getGroupCode,groupCode)
                        .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));
                // 任务也完成，则可跳转到复核列表页面
                switchPageVO.setToPage(NumConst.NUM_TWO);
                try {
                    log.info("自动出库");
                    outCheckService.autoOutConfirmation(wavePickingCode, outbound.getDeliveryCode(), NUM_ONE, NUM_ONE,groupCode);

                    log.info("异步释放容器");
                    outCheckService.releaseContainer(wavePickingCode,groupCode);
                } catch (Exception e) {
                    log.error("错误信息为：" + e.getMessage());
                }

            } else {
                // 更新出库单状态为复核中
                this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                        .eq(Outbound::getGroupCode,groupCode).set(Outbound::getStatus, OutboundStatusEnum.RECHECKING.getValue()));
            }
        } else {
            // 更新出库单状态为复核中
            this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                    .eq(Outbound::getGroupCode,groupCode).set(Outbound::getStatus, OutboundStatusEnum.RECHECKING.getValue()));
        }

        return switchPageVO;
    }

    private void updateDeliveryRecheckQuantity2(OutboundRegister register, String deliveryCode,String groupCode) {
        //更新分配表复核数量
        iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda().eq(DeliveryDistribution::getGroupCode,groupCode).eq(DeliveryDistribution::getDeliveryCode, deliveryCode)
                .eq(DeliveryDistribution::getOldInventoryCode, register.getOldInventoryCode())
                .setSql("recheck_quantity=picking_quantity-recheck_refund_quantity"));
        //更新订单明细复核数量
        iDeliveryItemService.updateRecheckQuantity(deliveryCode,groupCode,false);
        //更新订单复核数量
        iDeliveryService.updateRecheckQuantityByCode(deliveryCode,groupCode);

    }

    /**
     * @param dto :
     * @description PDA缺货登记
     * @author xuyang
     * @date 2020/10/29 9:59
     **/
    @Override
//    @Transactional(rollbackFor = {Exception.class})
    public PdaSwitchPageVO stockout(OutboundRegisterDTO dto) {
        String groupCode = UserUtil.getBranchCode();
        PdaSwitchPageVO switchPageVO = new PdaSwitchPageVO();
        // 默认PDA不跳转
        switchPageVO.setToPage(NumConst.NUM_ZERO);
        switchPageVO.setMsg("操作成功");

        // 检查缺货数量>0
        if (ObjectUtil.isNull(dto.getStockoutQuantity()) || BigDecimal.ZERO.compareTo(dto.getStockoutQuantity()) >= 0) {
            throw new ServiceException("缺货数量必须大于0");
        }
        // 更新任务状态
        iWmsTaskService.updateTaskWorkingByTaskCode(dto.getTaskCode());

        // 检查主单状态
        Outbound outbound = getOne(new QueryWrapper<Outbound>().lambda()
                .eq(Outbound::getGroupCode,groupCode).eq(Outbound::getCode, dto.getOutboundCode()).eq(Outbound::getGroupCode,groupCode).eq(Outbound::getDeleted, 0));
        if (ObjectUtil.isNull(outbound)) {
            throw new ServiceException(TipConst.OUTBOUND_NOT_EXIST);
        }
        if (!(outbound.getStatus().equals(OutboundStatusEnum.NOT_RECHECK.getValue()) || outbound.getStatus().equals(OutboundStatusEnum.RECHECKING.getValue()))) {
            throw new ServiceException("出库单已复核，无法再维护缺货数量");
        }

        OutboundItem item = iOutboundItemService.getOne(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,groupCode)
                .eq(OutboundItem::getCode, dto.getOutboundItemCode()));
        if (ObjectUtil.isNull(item)) {
            throw new ServiceException("出库单明细不存在");
        }

        // 尝试更新子单缺货量，如果执行成功，说明该数没有超过允许数量
        int updateStockoutQuantityFlag = iOutboundItemService.updateStockout(dto.getOutboundItemCode(), dto.getStockoutQuantity());
        if (updateStockoutQuantityFlag == 0) {
            throw new ServiceException("缺货数量超过了待拣货数量");
        }

        // 缺货登记返还库存
        iInventoryService.stockoutInventory(item.getInventoryCode(), dto.getStockoutQuantity(), StockoutTypeEnum.OUT_CHECK.getValue(), dto.getOutboundCode());

        // 更新明细下的未复核确认单的待复核数量
        iOutboundRegisterService.update(new UpdateWrapper<OutboundRegister>().lambda().eq(OutboundRegister::getOutboundItemCode, dto.getOutboundItemCode())
                .eq(OutboundRegister::getGroupCode,groupCode)
                .eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.NOT_RECHECK.getValue())
//                .setSql(" recheck_quantity = recheck_quantity - " + dto.getStockoutQuantity())
                .setSql(" not_recheck_quantity = not_recheck_quantity - " + dto.getStockoutQuantity()));
        // 更新主表缺货数量
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, dto.getOutboundCode())
                .eq(Outbound::getGroupCode, groupCode).setSql(" recheck_refund_quantity = recheck_refund_quantity + " + dto.getStockoutQuantity()));

        // 判断明细是否完成
        OutboundItem outboundItem = iOutboundItemService.getOne(new LambdaQueryWrapper<OutboundItem>().eq(OutboundItem::getCode, dto.getOutboundItemCode()));

        if (OutboundRecheckStatusEnum.RECHECKED.getValue().equals(outboundItem.getStatus())) {
            // 明细复核完成，则可跳转到复核详情页面
            switchPageVO.setToPage(NUM_ONE);

            // 删除明细下待复核的数据
            iOutboundRegisterService.remove(new QueryWrapper<OutboundRegister>().lambda()
                    .eq(OutboundRegister::getOutboundItemCode, dto.getOutboundItemCode()).eq(OutboundRegister::getGroupCode,groupCode).eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.NOT_RECHECK));
            // 尝试更新主单的状态
            int finishOutboundFlag = this.updateStatusIfFinishRecheck(dto.getOutboundCode(), OutboundStatusEnum.NOT_OUT.getValue());
            if (finishOutboundFlag > 0) {
                // 更新任务状态
                iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getCode, dto.getTaskCode())
                        .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));

                // 主单也完成，则可跳转到复核列表页面
                switchPageVO.setToPage(NumConst.NUM_TWO);
            }
        }
        // 释放已不使用的拣货容器
        this.stockoutReleasePickContainer(dto.getOutboundItemCode());

        // 更新发货单分配数量
        updateDistributionQuantityByOutItem(outbound, item);

        return switchPageVO;
    }

    /**
     * @param outboundCode:
     * @param status:
     * @description 根据总复核量+总退减量 与 计划复核量的比较，尝试更新出库单状态
     * @author xuyang
     * @date 2020/10/29 9:08
     **/
    private int updateStatusIfFinishRecheck(String outboundCode, String status) {
        return this.baseMapper.updateStatusIfFinishRecheck(outboundCode, status,UserUtil.getBranchCode());
    }

    /**
     * @param outboundVO:
     * @description 完成任务
     * @author xuyang
     * @date 2020/10/28 13:31
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Result finishTask(OutboundVO outboundVO, BigDecimal jobQuantity,String operator,String operatorName) {
        // 将任务设置为已完成、作业量为合计数量
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda()
                .eq(WmsTask::getSourceCode, outboundVO.getWavePickingCode())
                .eq(WmsTask::getGroupCode,outboundVO.getGroupCode())
                .set(WmsTask::getJobQuantity, jobQuantity)
                .set(WmsTask::getOperatorBy, operator)
                .set(WmsTask::getOperatorName, operatorName)
                .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));
        return Result.ok();
    }

    /**
     * @param itemsRecheckQuantity:
     * @description 校验复核数量超过计划量（拣货量），超过则有问题，回滚报错
     * @author xuyang
     * @date 2020/10/28 9:31
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Result updateItemForRecheck(Map<OutboundItem, BigDecimal> itemsRecheckQuantity) {
        // 遍历，判断复核数量是否超出，未超出则退减，并更新明细；超出则报错，回滚
        OutboundItem item;
        BigDecimal recheckQuantity;
        BigDecimal recheckUnitQuantity;
        for (Map.Entry<OutboundItem, BigDecimal> mapItem : itemsRecheckQuantity.entrySet()) {
            item = mapItem.getKey();
            recheckQuantity = mapItem.getValue();
            // 计算差异量 = 拣货量 - 退减数量 - （已复核数 + 待复核数）
            BigDecimal diff = item.getPlanQuantity().subtract(item.getReturnQuantity()).subtract(recheckQuantity);
            if (diff.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("复核数量与退减数量总和超过了实际拣货量，请确认！");
            } else if (diff.compareTo(BigDecimal.ZERO) > 0) {
                // 缺货登记返还库存
                iInventoryService.stockoutInventory(item.getInventoryCode(), diff, StockoutTypeEnum.OUT_CHECK.getValue(), item.getOutboundCode());
                // 释放已不使用的拣货容器
                this.stockoutReleasePickContainer(item.getCode());
            }

            recheckUnitQuantity = recheckQuantity.divide(item.getBasicQuantity(), 6, BigDecimal.ROUND_HALF_UP);
            // 更新退减数量(分配数量-实际复核数量）、更新复核数量、状态
            iOutboundItemService.update(new UpdateWrapper<OutboundItem>().lambda().eq(OutboundItem::getCode, item.getCode())
                    .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                    .set(OutboundItem::getRecheckQuantity, recheckQuantity)
                    .set(OutboundItem::getRecheckUnitQuantity, recheckUnitQuantity)
                    .set(OutboundItem::getStatus, OutboundRecheckStatusEnum.RECHECKED.getValue())
                    .setSql(" recheck_refund_quantity = plan_quantity - " + recheckQuantity)
                    .setSql(" return_quantity = recheck_refund_quantity"));
            //更新订单登记表的复核数量
            iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>().eq(DeliveryDistribution::getCode, item.getDeliveryDistributionCode())
                    .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                    .set(DeliveryDistribution::getRecheckQuantity, recheckQuantity)
                    .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue())
                    .setSql("recheck_refund_quantity = picking_quantity - recheck_quantity")
                    .setSql("not_recheck_quantity = not_recheck_quantity - recheck_quantity - recheck_refund_quantity"));
            //更新订单明细的复核数量
            iDeliveryItemService.update(new LambdaUpdateWrapper<DeliveryItem>().eq(DeliveryItem::getCode, item.getDeliveryItemCode())
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                    .set(DeliveryItem::getRecheckQuantity, recheckQuantity)
                    .set(DeliveryItem::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue())
                    .setSql("recheck_refund_quantity = picking_quantity - recheck_quantity")
                    .setSql("not_recheck_quantity = not_recheck_quantity - recheck_quantity - recheck_refund_quantity"));
        }

        return Result.ok();
    }

    /**
     * @param outboundVO:
     * @param isBatch:
     * @description 计算出各明细的实际复核数量
     * @author xuyang
     * @date 2020/10/28 9:22
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Map<OutboundItem, BigDecimal> calcRecheckQuantity(OutboundVO outboundVO, boolean isBatch) {
        List<OutboundItemWithRegisterVO> itemWithRegisterVOS = outboundVO.getOutboundItemWithRegisterVOList();
//        if (isBatch) {
        //更新明细的复核数量   复核数量=待复核数量
//            itemWithRegisterVOS.stream().forEach(item -> item.setRecheckQuantity(item.getNotRecheckQuantity()));
//        }
        // 根据状态过滤，获取未保存的数据
        List<OutboundRegister> unSaveRegisters = itemWithRegisterVOS.stream().filter(e -> e.getStatus().equals(OutboundRecheckStatusEnum.NOT_RECHECK.getValue()))
                .map(e -> {
                    if (e.getRecheckQuantity() == null || BigDecimal.ZERO.compareTo(e.getRecheckQuantity()) > 0) {
                        throw new ServiceException("请填写正确的复核数量");
                    }
                    OutboundRegister register = new OutboundRegister();
                    BeanUtils.copyProperties(e, register);
                    return register;
                }).collect(Collectors.toList());
        // 数据库后台获取最新的已复核数据
        List<OutboundRegister> savedRegisters = iOutboundRegisterService.list(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getDeleted, 0).eq(OutboundRegister::getOutboundCode, outboundVO.getCode())
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.RECHECKED.getValue()));
        // 获取最新的item列表
        List<OutboundItem> items = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,UserUtil.getBranchCode()).eq(OutboundItem::getDeleted, 0)
                .eq(OutboundItem::getOutboundCode, outboundVO.getCode()));
        // 合并未复核、已复核数据
        savedRegisters.addAll(unSaveRegisters);
        // 根据出库单明细汇总实际复核总数
        Map<String, BigDecimal> recheckQuantity = new HashMap<>();
        savedRegisters.forEach(e -> {
            recheckQuantity.put(e.getOutboundItemCode(), recheckQuantity.getOrDefault(e.getOutboundItemCode(), BigDecimal.ZERO).add(e.getRecheckQuantity()));
        });
        Map<OutboundItem, BigDecimal> itemWithQuantity = items.stream().collect(Collectors.toMap(e -> e, e -> recheckQuantity.getOrDefault(e.getCode(), BigDecimal.ZERO)));

        return itemWithQuantity;
    }

    /**
     * @param outboundVO:
     * @description PC端复核时保存本次新复核的数据
     * @author xuyang
     * @date 2020/10/28 8:43
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Result savePCUnSaveRegister(OutboundVO outboundVO) {
        List<OutboundItemWithRegisterVO> itemWithRegisterVOS = outboundVO.getOutboundItemWithRegisterVOList();
        WmsTask task = iWmsTaskService.getOne(new QueryWrapper<WmsTask>().lambda()
                .eq(WmsTask::getGroupCode,UserUtil.getBranchCode()).eq(WmsTask::getSourceCode, outboundVO.getWavePickingCode()));

        // 根据状态过滤，获取未保存的数据
        List<OutboundRegister> unSaveRegisters = itemWithRegisterVOS.stream().filter(e -> e.getStatus().equals(OutboundRecheckStatusEnum.NOT_RECHECK.getValue()))
                .map(e -> {
                    OutboundRegister register = new OutboundRegister();
                    BeanUtils.copyProperties(e, register);
                    register.setCode(IdUtil.simpleUUID());
                    register.setCreatedBy(UserUtil.getCode());
                    register.setCreatedName(partyCache.translate(UserUtil.getCode()));
                    register.setModifiedBy(StrUtil.EMPTY);
                    register.setModifiedName(StrUtil.EMPTY);
                    register.setStatus(OutboundRecheckStatusEnum.RECHECKED.getValue());
                    register.setGroupCode(UserUtil.getBranchCode());
                    // 如果已有任务，则将填写任务信息
                    if (task != null) {
                        register.setTaskCode(task.getCode());
                        register.setOperatorBy(task.getOperatorBy());
                        register.setOperatorName(task.getOperatorName());
                    }
                    return register;
                }).collect(Collectors.toList());
        if (!unSaveRegisters.isEmpty()) {
            iOutboundRegisterService.saveBatch(unSaveRegisters);
        }
        return Result.ok();
    }


    /**
     * @param outboundItemCode:
     * @description 缺货登记后，释放已经没用的拣货容器
     * @author xuyang
     * @date 2020/10/29 13:36
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Result stockoutReleasePickContainer(String outboundItemCode) {
        // 查询出库单明细
        OutboundItem item = iOutboundItemService.getOne(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,UserUtil.getBranchCode()).eq(OutboundItem::getCode, outboundItemCode));
        // 判断是否有拣货容器
        if (StrUtil.isBlank(item.getContainerBarCode())) {
            return Result.ok();
        }

        // 查询这个出库单下所有出库确认单相同、容器号相同的、未删除的数据
        List<OutboundRegister> registers = iOutboundRegisterService.list(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getOutboundCode, item.getOutboundCode()).eq(OutboundRegister::getContainerBarCode, item.getContainerBarCode())
                .eq(OutboundRegister::getDeleted, 0));
        // 判断是否有值
        if (registers.isEmpty()) {
            // 没有则可以释放
            iContainerService.releaseContainer(item.getContainerBarCode(),UserUtil.getBranchCode());
        }
        return Result.ok();
    }


    /**
     * @param outboundCode:
     * @description 出库确认时，释放拣货时占用的容器
     * @author xuyang
     * @date 2020/10/29 13:57
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Result outReleasePickContainer(String outboundCode) {
        // 获取出库确认单
        List<OutboundRegister> registers = iOutboundRegisterService.list(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getOutboundCode, outboundCode).eq(OutboundRegister::getDeleted, 0));
        // 汇总容器号，用map去重
        Map<String, String> containers = registers.stream().filter(e -> StrUtil.isNotBlank(e.getContainerBarCode())).collect(Collectors.toMap(e -> e.getCode(), e -> e.getContainerBarCode()));
        // 遍历，释放容器
        for (String containerBarCode : containers.values()) {
            iContainerService.releaseContainer(containerBarCode,UserUtil.getBranchCode());
        }
        return Result.ok();
    }

    /**
     * @param outboundCode:
     * @description 出库确认时，释放库存容器
     * @author xuyang
     * @date 2020/10/29 14:32
     **/
    @Transactional(rollbackFor = {Exception.class})
    public Result outReleaseInvContainer(String outboundCode,String groupCode) {
        // 获取出库确认单
        List<OutboundRegister> registers = iOutboundRegisterService.list(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,groupCode)
                .eq(OutboundRegister::getOutboundCode, outboundCode).eq(OutboundRegister::getDeleted, 0));
        // 汇总容器号，用map去重
        Map<String, Object> containers = new HashMap<>();
        registers.stream().forEach(e -> {
            if (StrUtil.isNotBlank(e.getInventoryContainerBarCode())) {
                containers.put(e.getInventoryContainerBarCode(), e);
            }
        });
        for (String containerBarCode : containers.keySet()) {
            // 查询库存中是否还有该容器在使用
            boolean noUseFlag = iInventoryService.noUseContainerBarCode(containerBarCode);
            if (noUseFlag) {
                // 释放容器
                iContainerService.releaseContainer(containerBarCode,groupCode);
            }
        }
        return Result.ok();
    }


    /**
     * 一周内所有仓库的入库数量
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> outboundCount(List<String> warehouseCodes, List<String> shipmentCodes,String groupCode) {
        return baseMapper.outboundCount(warehouseCodes, shipmentCodes,groupCode);
    }

    /**
     * @param dto :
     * @description PDA查询复核列表
     * @author xuyang
     * @date 2020/12/10 15:52
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public WmsTaskVO pdaRecheckList(OutboundRegisterDTO dto, WmsTask task) {
        WmsTaskVO vo = new WmsTaskVO();
        // 校验参数
        //注意：这里的outboundCode对应的是波次号
        String wavePickingCode = dto.getOutboundCode();
        if (ObjectUtil.isNull(wavePickingCode)) {
            throw new ServiceException("波次号参数为空");
        }
        BeanUtil.copyProperties(task, vo);

        // 获取客户信息
        List<Outbound> outboundList = list(new QueryWrapper<Outbound>().lambda().eq(Outbound::getWavePickingCode, wavePickingCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        if (IterUtil.isEmpty(outboundList)) {
            throw new ServiceException(TipConst.OUTBOUND_NOT_EXIST);
        }
        Outbound firstOutbound = outboundList.get(NUM_ZERO);
        vo.setShipmentName(firstOutbound.getShipmentName());
        vo.setCustomerCode(firstOutbound.getCustomerCode());
        vo.setCustomerName(customerCache.translate(firstOutbound.getCustomerCode()));
        vo.setWavePickingCode(firstOutbound.getWavePickingCode());
        // 查询 未复核/已复核 的数据
        if (OutboundRecheckStatusEnum.NOT_RECHECK.getValue().equals(dto.getStatus())) {
            // 查询未复核的列表
            OutboundItemDTO itemDTO = new OutboundItemDTO();
            itemDTO.setStatus(OutboundRecheckStatusEnum.NOT_RECHECK.getValue());
            itemDTO.setWavePickingCode(wavePickingCode);
            itemDTO.setGoodsBarCode(dto.getGoodsBarCode());
            List<OutboundItemVO> outboundItemVOS = iOutboundItemService.queryListGroupByWavePickingCodeAndStatus(itemDTO);
            // 将item转换为registerVO对象
            List<OutboundRegisterVO> outboundRegisterVOS = outboundItemVOS.stream().map(e -> {
                OutboundRegisterVO registerVO = new OutboundRegisterVO();
                BeanUtils.copyProperties(e, registerVO);
                registerVO.setTaskCode(task.getCode());
                return registerVO;
            }).collect(Collectors.toList());
            vo.setOutboundRegisterVOS(outboundRegisterVOS);
        } else {
            // 查询已复核的数据
            dto.setWavePickingCode(wavePickingCode);
            List<OutboundRegisterVO> outboundRegisterVOS = iOutboundRegisterService.queryListGroupByWavePickingCodeAndStatus(dto);
            vo.setOutboundRegisterVOS(outboundRegisterVOS);
        }

        // 添加批次规则
        if (IterUtil.isNotEmpty(vo.getOutboundRegisterVOS())) {
            vo.getOutboundRegisterVOS().stream().forEach(item -> {
                Map<String, Object> goodsCacheMap = goodsCache.get(item.getGoodsCode());
                Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(LOT_NUMBER));
                item.setBatchRule(batchRuleMap);
            });
        }
        return vo;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public WmsTaskVO pdaRecheckListForQuantity(OutboundRegisterDTO dto, WmsTask task) {
        WmsTaskVO vo = new WmsTaskVO();
        // 校验参数
        if (ObjectUtil.isNull(dto.getOutboundCode())) {
            throw new ServiceException("出库单号参数为空");
        }
        BeanUtil.copyProperties(task, vo);

        // 获取客户信息
        Outbound outbound = getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, dto.getOutboundCode())
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        if (ObjectUtil.isNull(outbound)) {
            throw new ServiceException(TipConst.OUTBOUND_NOT_EXIST);
        }
        vo.setShipmentName(outbound.getShipmentName());
        vo.setCustomerCode(outbound.getCustomerCode());
        vo.setCustomerName(customerCache.translate(outbound.getCustomerCode()));
        vo.setWavePickingCode(outbound.getWavePickingCode());

        //根据pn获取商品code
        if (StrUtil.isNotBlank(dto.getGoodsBarCode())) {
            if (StrUtil.isNotBlank(outbound.getShipmentCode())) {
                String goodsCode = goodsPnService.getGoodsCode(outbound.getShipmentCode(), dto.getGoodsBarCode());
                if (StrUtil.isNotBlank(goodsCode)) {
                    dto.setGoodsBarCode("");
                    dto.setGoodsCode(goodsCode);
                }
            }
        }
        // 查询 未复核/已复核 的数据
        if (OutboundRecheckStatusEnum.NOT_RECHECK.getValue().equals(dto.getStatus())) {
            // 查询未复核的列表
            OutboundItemDTO itemDTO = new OutboundItemDTO();
            BeanUtils.copyProperties(dto, itemDTO);
            itemDTO.setStatuses(new ArrayList<>(Arrays.asList(OutboundRecheckStatusEnum.NOT_RECHECK.getValue(),OutboundRecheckStatusEnum.RECHECKING.getValue())));
            //将状态设置为空，通过statuses来进行筛选
            itemDTO.setStatus(StrUtil.EMPTY);
            List<OutboundItemVO> outboundItemVOS = iOutboundItemService.queryList(itemDTO);
            // 将item转换为registerVO对象
            List<OutboundRegisterVO> outboundRegisterVOS = outboundItemVOS.stream().map(e -> {
                OutboundRegisterVO registerVO = new OutboundRegisterVO();
                BeanUtils.copyProperties(e, registerVO);
                registerVO.setTaskCode(task.getCode());
                return registerVO;
            }).collect(Collectors.toList());
            vo.setOutboundRegisterVOS(outboundRegisterVOS);
        } else {
            // 查询已复核的数据
            List<OutboundRegisterVO> outboundRegisterVOS = iOutboundRegisterService.queryRegisterList(dto);
            vo.setOutboundRegisterVOS(outboundRegisterVOS);
        }

        // 添加批次规则
        if (IterUtil.isNotEmpty(vo.getOutboundRegisterVOS())) {
            vo.getOutboundRegisterVOS().stream().forEach(item -> {
                Map<String, Object> goodsCacheMap = goodsCache.get(item.getGoodsCode());
                Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(LOT_NUMBER));
                item.setBatchRule(batchRuleMap);
            });
        }
        return vo;
    }


    /**
     * @param registerDTO :
     * @description PDA查询复核详细信息(非按单复核走此方法)
     * @author xuyang
     * @date 2020/12/10 16:02
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result pdaRecheckDetail(OutboundRegisterDTO registerDTO) {
        // 根据确认单code、商品条码 查询, 当商品条码不为空时，需要根据整个出库单查询
        List<OutboundRegister> registers = iOutboundRegisterService.list(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getWavePickingCode, registerDTO.getSourceCode())
                .eq(StrUtil.isNotBlank(registerDTO.getGoodsBarCode()), OutboundRegister::getGoodsBarCode, registerDTO.getGoodsBarCode()));
        if (IterUtil.isEmpty(registers)) {
            throw new ServiceException("复核商品信息为空");
        }
        BigDecimal notRecheckQuantity = registers.stream().map(OutboundRegister::getPlanQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 获取未复核的信息，可能会查询到多个，前端只使用列表的第一个对象即可
        OutboundRegister registerOne = registers.get(NumConst.NUM_ZERO);
        OutboundRegisterVO registerVO = new OutboundRegisterVO();
        BeanUtils.copyProperties(registerOne, registerVO);
        registerVO.setNotRecheckQuantity(notRecheckQuantity);
        registerVO.setLocationName(locationCache.translate(registerVO.getLocation()));
        // 添加批次规则
        Map<String, Object> goodsCacheMap = goodsCache.get(registerVO.getGoodsCode());
        Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(LOT_NUMBER));
        registerVO.setBatchRule(batchRuleMap);

        return Result.ok().data(registerVO);
    }

    /**
     * @param registerDTO :
     * @description PDA查询复核详细信息(按单复核走此方法)
     * @author xuyang
     * @date 2020/12/10 16:02
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result pdaRecheckDetailForQuantity(OutboundRegisterDTO registerDTO) {


        LambdaQueryWrapper<OutboundRegister> query = Wrappers.lambdaQuery();
        query.eq(OutboundRegister::getOutboundCode, registerDTO.getSourceCode())
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(StrUtil.isNotBlank(registerDTO.getOutboundCode()), OutboundRegister::getOutboundCode, registerDTO.getSourceCode());
        if (StrUtil.isBlank(registerDTO.getGoodsBarCode())) {
            query.eq(OutboundRegister::getOutboundItemCode, registerDTO.getCode());
        } else {
            String goodsCode = "";
            if (StrUtil.isNotBlank(registerDTO.getGoodsBarCode())) {
                Outbound outbound = baseMapper.selectOne(new LambdaQueryWrapper<Outbound>()
                        .select(Outbound::getShipmentCode).eq(Outbound::getCode, registerDTO.getSourceCode())
                        .eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
                if (outbound != null && StrUtil.isNotBlank(outbound.getShipmentCode())) {
                    goodsCode = goodsPnService.getGoodsCode(outbound.getShipmentCode(), registerDTO.getGoodsBarCode());
                }
                if (StrUtil.isNotBlank(goodsCode)) {
                    query.eq(OutboundRegister::getGoodsCode, goodsCode);
                } else {
                    query.eq(OutboundRegister::getGoodsBarCode, registerDTO.getGoodsBarCode());
                }

            }
        }

        // 根据确认单code、商品条码 查询, 当商品条码不为空时，需要根据整个出库单查询
        List<OutboundRegister> registers = iOutboundRegisterService.list(query);

        if (IterUtil.isEmpty(registers)) {
            throw new ServiceException("复核商品信息为空");
        }

        // 获取未复核的信息，可能会查询到多个，前端只使用列表的第一个对象即可
        OutboundRegister registerOne = registers.get(NumConst.NUM_ZERO);
        OutboundRegisterVO registerVO = new OutboundRegisterVO();
        BeanUtils.copyProperties(registerOne, registerVO);
        registerVO.setLocationName(locationCache.translate(registerVO.getLocation()));
        // 添加批次规则
        Map<String, Object> goodsCacheMap = goodsCache.get(registerVO.getGoodsCode());
        Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(LOT_NUMBER));
        registerVO.setBatchRule(batchRuleMap);

        return Result.ok().data(registerVO);
    }


    /**
     * 根据波次号和库存code查询发货分配信息
     *
     * @param query
     * @return
     */
    @Override
    public List<DeliveryDistributionVO> queryDistributionsByInventoryCodeAndWavePickingCode(DeliveryDistributionDTO query) {
        //批量拣货查询分配信息
        return iDeliveryDistributionService.queryListByInventoryCode(query, PropertyConst.OUT_BOUND);
    }

    /**
     * @param outboundVO:
     * @description PC端批量复核出库单
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void batchRecheck(OutboundVO outboundVO) {
        String groupCode = UserUtil.getBranchCode();
        //高并发下调用工具类提前
        String operator = UserUtil.getCode();
        String operatorName = partyCache.translate(operator);
        outboundVO.setGroupCode(groupCode);
        log.info("出库单复核：{}", JSONUtil.toJsonStr(outboundVO));
        // 获取数据库中的出库单
        Outbound outbound = this.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, outboundVO.getCode()).eq(Outbound::getGroupCode,groupCode));
        // 判断出库单是否存在、状态是否正确
        if (outbound == null) {
            throw new ServiceException("出库单不存在，请确认");
        }
        if (!StrUtil.equalsAny(outbound.getStatus(), OutboundStatusEnum.NOT_RECHECK.getValue(), OutboundStatusEnum.RECHECKING.getValue())) {
            throw new ServiceException("出库单已复核，不能重复复核");
        }

        // 删除待确认确认单
        iOutboundRegisterService.remove(new QueryWrapper<OutboundRegister>().lambda()
                .eq(OutboundRegister::getGroupCode,groupCode)
                .eq(OutboundRegister::getOutboundCode, outboundVO.getCode()).eq(OutboundRegister::getStatus, OutboundRecheckStatusEnum.NOT_RECHECK));

        // 更新明细信息，如果复核数量有异，则会回滚的，缺货则会退减
        Map<OutboundItem, BigDecimal> itemsRecheckQuantity = calcRecheckQuantity(outboundVO, true);

        // 保存前端未复核状态的数据
        savePCUnSaveRegister(outboundVO);

        // 更新明细的复核数量
        updateItemForRecheck(itemsRecheckQuantity);
        BigDecimal recheckQuantitySum = BigDecimal.ZERO;
        for (BigDecimal val : itemsRecheckQuantity.values()) {
            recheckQuantitySum = recheckQuantitySum.add(val);
        }
        finishTask(outboundVO, recheckQuantitySum,operator,operatorName);

        // 更新出库主单状态、复核数量、退减数量
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outboundVO.getCode())
                .eq(Outbound::getGroupCode,groupCode)
                .set(Outbound::getRecheckQuantity, recheckQuantitySum)
                .setSql(" recheck_refund_quantity = plan_quantity -" + recheckQuantitySum)
                .set(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        //更新订单分配表的复核数量
        iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda().eq(DeliveryDistribution::getGroupCode,groupCode).eq(DeliveryDistribution::getWavePickingCode, outboundVO.getWavePickingCode())
                .setSql("recheck_quantity=picking_quantity-recheck_refund_quantity"));
        //将订单状态为”待复核“的更新为”待出库“
        List<Delivery> deliveryList = iDeliveryService.list(new LambdaQueryWrapper<Delivery>()
                .eq(Delivery::getGroupCode,groupCode)
                .select(Delivery::getCode)
                .eq(Delivery::getWavePickingCode, outboundVO.getWavePickingCode())
                .eq(Delivery::getDeleted, NUM_ZERO)
                .eq(Delivery::getStatus, DeliveryStatusEnum.UN_RECHECK.getValue()));
        if (IterUtil.isEmpty(deliveryList)) {
            return;
        }
        Set<String> deliveryCodes = deliveryList.stream().map(Delivery::getCode).collect(Collectors.toSet());

        iDeliveryService.updateRecheckQuantityAndStatus(deliveryCodes, DeliveryStatusEnum.UN_DELIVERY.getValue());

    }

    /**
     * 出库单 打印面单
     *
     * @param outboundCodes
     * @return
     */
    @Override
    public List<DeliveryVO> printExpress(List<String> outboundCodes) {
        List<Outbound> outboundList = this.list(new QueryWrapper<Outbound>().lambda().in(Outbound::getCode, outboundCodes).eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        long count = outboundList.stream().filter(item -> !OutboundStatusEnum.FINISH_OUT.getValue().equals(item.getStatus())).count();
        if (count > NUM_ZERO) {
            throw new ServiceException(HAVE + count + ITEM + NOT_COMPLETE + STATUS_NAME + OUTBOUND);
        }
        List<DeliveryVO> deliveryVOList = Lists.newArrayList();
        outboundList.forEach(item -> {
            // 不是波次出库单
            if (ObjectUtil.isEmpty(item.getWavePickingCode())) {
                DeliveryVO deliveryVO = iDeliveryService.getVOByCode(item.getDeliveryCode());

                Carrier carrier = carrierService.getOne(new LambdaQueryWrapper<Carrier>().select(Carrier::getPlatformType).eq(Carrier::getGroupCode,UserUtil.getBranchCode()).eq(Carrier::getCode, deliveryVO.getCarrierCode()).last("limit 1"));
                if (carrier != null) {
                    deliveryVO.setPlatformType(carrier.getPlatformType());
                }
                deliveryVOList.add(deliveryVO);
            }
            // 波次出库单
            if (ObjectUtil.isNotEmpty(item.getWavePickingCode())) {
                DeliveryDTO deliveryDTO = new DeliveryDTO();
                deliveryDTO.setWavePickingCode(item.getWavePickingCode());
                deliveryDTO.setPageSize(Integer.MAX_VALUE);
                List<DeliveryVO> deliveryVOList1 = iDeliveryService.queryList(deliveryDTO);
                Set<String> carrierCodeSet = deliveryVOList1.stream().map(DeliveryVO::getCarrierCode).collect(Collectors.toSet());
                if (IterUtil.isEmpty(carrierCodeSet)) {
                    throw new ServiceException("找不到承运商");
                }
                if (carrierCodeSet.size() > NUM_ONE) {
                    throw new ServiceException(MUST + CHOOSE + SAME + LOGISTICS_NAME + DELIVERY);
                }
                Carrier carrier = carrierService.getOne(new LambdaQueryWrapper<Carrier>().eq(Carrier::getGroupCode,UserUtil.getBranchCode()).select(Carrier::getPlatformType).in(Carrier::getCode, carrierCodeSet).last("limit 1"));
                deliveryVOList1.forEach(r -> {
                    if (carrier != null) {
                        r.setPlatformType(carrier.getPlatformType());
                    }
                });
                deliveryVOList.addAll(deliveryVOList1);
            }
        });

        return deliveryVOList;
    }




    /**
     * 查询出库单信息
     */
    @Override
    public OutboundVO getOutboundInfo(String outboundCode) {
        Outbound outbound = this.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, outboundCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        OutboundVO outboundVO = new OutboundVO();
        BeanUtil.copyProperties(outbound, outboundVO);
        outboundVO.setLogisticsName(LogisticsUtils.translate(outboundVO.getLogisticsCode()));
        return outboundVO;
    }

    /**
     * @param vo
     * @description PDA端(单品单个 、 单品多个 、 多品)复核
     * @author rfwang
     * @date 2021/05/07 13:10
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public PdaSwitchPageVO pdaSingleDeliveryRecheck(DeliveryItemWithDistributionVO vo) {
        String groupCode = vo.getGroupCode();
        PdaSwitchPageVO switchPageVO = new PdaSwitchPageVO();
        // 默认PDA不跳转
        switchPageVO.setToPage(NumConst.NUM_ZERO);
        switchPageVO.setMsg("操作成功");

        // 校验复核数量，必须>0
        if (ObjectUtil.isNull(vo.getRecheckQuantity()) || BigDecimal.ZERO.compareTo(vo.getRecheckQuantity()) >= 0) {
            throw new ServiceException("复核数量必须大于0");
        }
        String wavePickingCode = vo.getWavePickingCode();
        //检查主单状态
        Delivery delivery = iDeliveryService.getOne(new LambdaQueryWrapper<Delivery>().eq(Delivery::getCode, vo.getDeliveryCode())
                .eq(Delivery::getGroupCode,groupCode));
        if (ObjectUtil.isNull(delivery)) {
            throw new ServiceException("出库单不存在");
        }
        if (!StrUtil.equalsAny(delivery.getStatus(),DeliveryStatusEnum.UN_RECHECK.getValue(),DeliveryStatusEnum.RECHECKING.getValue())) {
            throw new ServiceException("出库单非待复核、复核中状态，无法复核确认");
        }
        Outbound outbound = this.getOne(new LambdaQueryWrapper<Outbound>().eq(Outbound::getDeliveryCode, delivery.getCode())
                .eq(Outbound::getGroupCode,groupCode));
        WmsTask task = iWmsTaskService.getOne(new QueryWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,groupCode));
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("任务信息不存在");
        }

        // 更新任务状态, 此时也会自动占有任务
        iWmsTaskService.updateTaskWorkingBySourceCode(wavePickingCode);
        // 计算出库单明细数量是否超出
        DeliveryItem deliveryItem = iDeliveryItemService.getOne(new LambdaQueryWrapper<DeliveryItem>()
                .eq(DeliveryItem::getGroupCode,groupCode)
                .eq(DeliveryItem::getCode, vo.getDeliveryItemCode()));
        BigDecimal diffQuantity = deliveryItem.getPickingQuantity().subtract(deliveryItem.getRecheckRefundQuantity()).subtract(deliveryItem.getRecheckQuantity());
        if (diffQuantity.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("订单复核数量与缺货数量之和超过了该明细的总复核数量，无法进行复核确认");
        }
        if (diffQuantity.compareTo(vo.getRecheckQuantity()) < 0) {
            throw new ServiceException("商品的复核数量不能超过待复核数量");
        }

        //更新复核单、复核明细、复核登记的复核数量、待复核数量和状态
        updateOutboundQuantityAndStatus(outbound.getCode(), vo.getRecheckQuantity(), vo.getOldInventoryCode(),groupCode);

        //更新订单复核数量及状态
        updateDeliveryRecheckQuantity(vo);
        // 更新任务作业量，作业状态在占用作业时就已更新，所以不需要再更新
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, outbound.getWavePickingCode())
                .eq(WmsTask::getGroupCode,groupCode)
                .setSql(" job_quantity = job_quantity + " + vo.getRecheckQuantity()));

        // 尝试更新明细完成状态
        int finishItemFlag = iDeliveryItemService.updateStatusIfFinishRecheck(vo.getDeliveryItemCode(), DeliveryStatusEnum.UN_DELIVERY.getValue());
        if (finishItemFlag > 0) {
            // 明细复核完成，则可跳转到复核详情页面
            switchPageVO.setToPage(NUM_ONE);

            // 尝试更新主单的状态
            int finishDeliveryFlag = iDeliveryService.updateStatusIfFinishRecheck(vo.getDeliveryCode(), DeliveryStatusEnum.UN_DELIVERY.getValue());

            if (finishDeliveryFlag > 0) {
                switchPageVO.setToPage(NumConst.NUM_TWO);
                //由于前面更新出库单复核数量的时候更新了状态，会导致出库单再次更新出现失败的情况，所以直接查询出库单状态即可
                Outbound outboundNew = this.getOne(new LambdaQueryWrapper<Outbound>().eq(Outbound::getCode, outbound.getCode()).eq(Outbound::getGroupCode,groupCode));


                if (OutboundStatusEnum.NOT_OUT.getValue().equals(outboundNew.getStatus())) {

                    //复核过程中，不抛出出库确认的问题
                    try {
                        //4.出库确认
                        //自动出库
                        log.info("自动出库");
                        outCheckService.autoOutConfirmation(wavePickingCode, StrUtil.EMPTY, NUM_ONE, NUM_ONE,groupCode);

                        log.info("异步释放容器");
                        outCheckService.releaseContainer(wavePickingCode,groupCode);
                    } catch (Exception e) {
                        log.error("错误信息为：" + e.getMessage());
                    }
                }
                List<String> statusList = new ArrayList<>(Arrays.asList(OutboundStatusEnum.NOT_OUT.getValue(), OutboundStatusEnum.FINISH_OUT.getValue()));
                long totalOutboundQuantity = this.count(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                        .eq(Outbound::getGroupCode,groupCode));
                long recheckedQuantity = this.count(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                        .eq(Outbound::getGroupCode,groupCode)
                        .in(Outbound::getStatus, statusList));
                if (totalOutboundQuantity == recheckedQuantity) {
                    //更新任务状态
                    iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getCode, task.getCode())
                            .eq(WmsTask::getGroupCode,groupCode)
                            .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));
                    // 任务也完成，则可跳转到复核列表页面
                    switchPageVO.setToPage(NumConst.NUM_THREE);
                }

            } else {
                // 更新出库单状态为复核中
                this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                        .eq(Outbound::getGroupCode,groupCode).set(Outbound::getStatus, OutboundStatusEnum.RECHECKING.getValue()));
            }

        } else {
            // 更新出库单状态为复核中
            this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                    .eq(Outbound::getGroupCode,groupCode).set(Outbound::getStatus, OutboundStatusEnum.RECHECKING.getValue()));
        }

        return switchPageVO;

    }

    private void updateDeliveryRecheckQuantity(DeliveryItemWithDistributionVO vo) {
        if (null != vo.getRecheckQuantity() && vo.getRecheckQuantity().compareTo(BigDecimal.ZERO) > NUM_ZERO) {
            iDeliveryDistributionService.updateRechekQuantity(vo.getDeliveryCode(), vo.getOldInventoryCode(), vo.getRecheckQuantity(), BigDecimal.ZERO,UserUtil.getBranchCode());
            //更新订单明细的复核数量
            iDeliveryItemService.update(new LambdaUpdateWrapper<DeliveryItem>()
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                   .eq(DeliveryItem::getCode, vo.getDeliveryItemCode()).setSql("recheck_quantity =recheck_quantity +" + vo.getRecheckQuantity()).setSql("not_recheck_quantity = not_recheck_quantity -" + vo.getRecheckQuantity()));

            //更新订单的复核数量
            iDeliveryService.update(new LambdaUpdateWrapper<Delivery>().eq(Delivery::getCode, vo.getDeliveryCode())
                    .eq(Delivery::getGroupCode,UserUtil.getBranchCode()).setSql("recheck_quantity =recheck_quantity +" + vo.getRecheckQuantity()));

        }

        if (null != vo.getRecheckRefundQuantity() && vo.getRecheckRefundQuantity().compareTo(BigDecimal.ZERO) > NUM_ZERO) {
            iDeliveryDistributionService.updateRechekQuantity(vo.getDeliveryCode(), vo.getOldInventoryCode(), BigDecimal.ZERO, vo.getRecheckRefundQuantity(),UserUtil.getBranchCode());
            //更新订单明细的复核缺货数量
            iDeliveryItemService.update(new LambdaUpdateWrapper<DeliveryItem>().eq(DeliveryItem::getCode, vo.getDeliveryItemCode())
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                   .setSql("recheck_refund_quantity = recheck_refund_quantity + " + vo.getRecheckRefundQuantity()).setSql("not_recheck_quantity = not_recheck_quantity -" + vo.getRecheckRefundQuantity()));
            //更新订单的复核缺货数量
            iDeliveryService.update(new LambdaUpdateWrapper<Delivery>().eq(Delivery::getCode, vo.getDeliveryCode())
                    .eq(Delivery::getGroupCode,UserUtil.getBranchCode()).setSql("recheck_refund_quantity = recheck_refund_quantity +" + vo.getRecheckRefundQuantity()));
        }
    }

    /**
     * @param vo
     * @description 单品单件、单品多件、多品复核缺货登记
     * @author rfwang
     * @date 2021/05/07 13:10
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public PdaSwitchPageVO deliveryStockout(DeliveryItemWithDistributionVO vo) {
        String groupCode = UserUtil.getBranchCode();
        PdaSwitchPageVO switchPageVO = new PdaSwitchPageVO();
        // 默认PDA不跳转
        switchPageVO.setToPage(NumConst.NUM_ZERO);
        switchPageVO.setMsg("操作成功");

        // 检查缺货数量>0
        if (ObjectUtil.isNull(vo.getRecheckRefundQuantity()) || BigDecimal.ZERO.compareTo(vo.getRecheckRefundQuantity()) >= 0) {
            throw new ServiceException("缺货数量必须大于0");
        }

        //检查主单状态
        Delivery delivery = iDeliveryService.getOne(new LambdaQueryWrapper<Delivery>().eq(Delivery::getCode, vo.getDeliveryCode())
                .eq(Delivery::getGroupCode,groupCode));
        if (ObjectUtil.isNull(delivery)) {
            throw new ServiceException("出库单不存在");
        }
        if (!delivery.getStatus().equals(DeliveryStatusEnum.UN_RECHECK.getValue())) {
            throw new ServiceException("出库单非待复核状态，无法复核确认");
        }
        String wavePickingCode = delivery.getWavePickingCode();
        Outbound outbound = this.getOne(new LambdaQueryWrapper<Outbound>().eq(Outbound::getDeliveryCode, delivery.getCode())
                .eq(Outbound::getGroupCode,groupCode));
        WmsTask task = iWmsTaskService.getOne(new QueryWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,groupCode));
        // 更新任务状态
        iWmsTaskService.updateTaskWorkingBySourceCode(wavePickingCode);


        OutboundItem item = iOutboundItemService.getOne(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getGroupCode,groupCode).eq(OutboundItem::getOutboundCode, outbound.getCode()).eq(OutboundItem::getOldInventoryCode, vo.getOldInventoryCode()));
        if (ObjectUtil.isNull(item)) {
            throw new ServiceException("出库单明细不存在");
        }

        // 尝试更新子单缺货量，如果执行成功，说明该数没有超过允许数量
        int updateStockoutQuantityFlag = iOutboundItemService.updateStockout(item.getCode(), vo.getRecheckRefundQuantity());
        if (updateStockoutQuantityFlag == 0) {
            throw new ServiceException("缺货数量超过了待拣货数量");
        }

        // 缺货登记返还库存
        iInventoryService.stockoutInventory(vo.getInventoryCode(), vo.getRecheckRefundQuantity(), StockoutTypeEnum.OUT_CHECK.getValue(), outbound.getCode());

        // 更新明细下的未复核确认单的待复核数量
        List<String> registerStatuses = new ArrayList<>(Arrays.asList(OutboundRecheckStatusEnum.NOT_RECHECK.getValue(), OutboundRecheckStatusEnum.RECHECKING.getValue()));
        iOutboundRegisterService.update(new UpdateWrapper<OutboundRegister>().lambda().eq(OutboundRegister::getOutboundItemCode, item.getCode())
                .eq(OutboundRegister::getGroupCode,groupCode)
                .in(OutboundRegister::getStatus, registerStatuses)
                .setSql(" not_recheck_quantity = not_recheck_quantity - " + vo.getRecheckRefundQuantity())
                .setSql("recheck_refund_quantity = recheck_refund_quantity +" + vo.getRecheckRefundQuantity()));
        iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                .eq(OutboundRegister::getGroupCode,groupCode)
                .eq(OutboundRegister::getOutboundItemCode, item.getCode())
                .apply("plan_quantity = recheck_quantity + recheck_refund_quantity")
                .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        // 更新主表缺货数量
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                .eq(Outbound::getGroupCode,groupCode).setSql(" recheck_refund_quantity = recheck_refund_quantity + " + vo.getRecheckRefundQuantity()));

        //更新订单的复核数量
        updateDeliveryRecheckQuantity(vo);
        // 尝试更新明细完成状态
        int finishItemFlag = iDeliveryItemService.updateStatusIfFinishRecheck(vo.getDeliveryItemCode(), DeliveryStatusEnum.UN_DELIVERY.getValue());
        if (finishItemFlag > 0) {

            // 明细复核完成，则可跳转到复核详情页面
            switchPageVO.setToPage(NUM_ONE);


            // 尝试更新主单的状态
            int finishDeliveryFlag = iDeliveryService.updateStatusIfFinishRecheck(vo.getDeliveryCode(), DeliveryStatusEnum.UN_DELIVERY.getValue());

            if (finishDeliveryFlag > 0) {
                switchPageVO.setToPage(NumConst.NUM_TWO);
                // 尝试更新主单的状态
                int finishOutboundFlag = this.updateStatusIfFinishRecheck(outbound.getCode(), OutboundStatusEnum.NOT_OUT.getValue());
                if (finishOutboundFlag > 0) {
                    List<String> statusList = new ArrayList<>(Arrays.asList(OutboundStatusEnum.NOT_OUT.getValue(), OutboundStatusEnum.FINISH_OUT.getValue()));
                    long totalOutboundQuantity = this.count(new LambdaQueryWrapper<Outbound>()
                            .eq(Outbound::getGroupCode,groupCode).eq(Outbound::getWavePickingCode, wavePickingCode));
                    long recheckedQuantity = this.count(new LambdaQueryWrapper<Outbound>()
                            .eq(Outbound::getGroupCode,groupCode).eq(Outbound::getWavePickingCode, wavePickingCode)
                            .in(Outbound::getStatus, statusList));
                    if (totalOutboundQuantity == recheckedQuantity) {
                        //更新任务状态
                        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getCode, task.getCode())
                                .eq(WmsTask::getGroupCode,groupCode)
                                .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));
                        // 任务也完成，则可跳转到复核列表页面
                        switchPageVO.setToPage(NumConst.NUM_THREE);

                    }
                }


            } else {
                // 更新出库单状态为复核中
                this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getCode, outbound.getCode())
                        .eq(Outbound::getGroupCode,groupCode).set(Outbound::getStatus, OutboundStatusEnum.RECHECKING.getValue()));
            }
        }
        // 释放已不使用的拣货容器
        this.stockoutReleasePickContainer(item.getCode());

        // 更新发货单分配数量
        updateDistributionQuantityByOutItem(outbound, item);

        return switchPageVO;
    }

    /**
     * 根据出库单的计划数量和（复核数量+复核缺货数量）关系更新出库单复核数量和状态
     *
     * @param code 出库单号
     * @param code recheckQuantity
     * @author rfwang
     * @date 2021/5/21 12:13
     */
    @Override
    public int updateOutboundQuantityAndStatus(String code, BigDecimal recheckQuantity, String oldInventoryCode,String groupCode) {
        //更新出库登记的复核数量和状态
        iOutboundRegisterService.generalReviewUpdateInfo(code, oldInventoryCode, recheckQuantity,groupCode,false);
        //更新出库明细的复核数量和状态
        iOutboundItemService.updateRecheckQuantity(code,groupCode);
        //更新出库单的复核数量和状态
        return this.baseMapper.updateRecheckQuantity(code,groupCode);
    }




    /**
     * 处理订单完全缺货的逻辑
     *
     * @param deliveryCode 订单号
     * @param outboundCode 出库单号
     */
    private void handleWholeStockOutDelivery(String deliveryCode, String outboundCode,String groupCode) {
        //查询完全缺货的订单
        List<Delivery> deliveryList = iDeliveryService.list(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode,groupCode)
                .apply("total_num = recheck_refund_quantity + pick_stock_out_num").eq(Delivery::getDeleted, NUM_ZERO));
        if (IterUtil.isEmpty(deliveryList)) {
            return;
        }

        //抹除订单、订单明细的波次号等信息、删除分配单信息
        iDeliveryService.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode,groupCode).set(Delivery::getPickNum, BigDecimal.ZERO).set(Delivery::getWavePickingCode, StrUtil.EMPTY).set(Delivery::getPickStockOutNum, BigDecimal.ZERO).set(Delivery::getStatus, DeliveryStatusEnum.UN_DISTRIBUTION.getValue()).set(Delivery::getRecheckQuantity, BigDecimal.ZERO).set(Delivery::getRecheckRefundQuantity, BigDecimal.ZERO).set(Delivery::getDistributionNum, BigDecimal.ZERO));
        iDeliveryItemService.update(new UpdateWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getDeliveryCode, deliveryCode)
               .set(DeliveryItem::getPickingQuantity, BigDecimal.ZERO).eq(DeliveryItem::getGroupCode,groupCode).set(DeliveryItem::getPickStockOutNum, BigDecimal.ZERO).set(DeliveryItem::getWavePickingCode, StrUtil.EMPTY).set(DeliveryItem::getDistributionQuantity, BigDecimal.ZERO).set(DeliveryItem::getStatus, DeliveryStatusEnum.ADD.getValue()).set(DeliveryItem::getRecheckQuantity, BigDecimal.ZERO).set(DeliveryItem::getDistributionQuantity, BigDecimal.ZERO));
        iDeliveryDistributionService.remove(new QueryWrapper<DeliveryDistribution>().lambda().eq(DeliveryDistribution::getDeliveryCode, deliveryCode).eq(DeliveryDistribution::getGroupCode,groupCode));

        //调整出库单的计划数量并解除和订单的联系
        this.update(new UpdateWrapper<Outbound>().lambda().eq(Outbound::getDeliveryCode, deliveryCode)
                .eq(Outbound::getGroupCode,groupCode).set(Outbound::getDeliveryCode, BigDecimal.ZERO)
                .set(Outbound::getDeliveryCode, StrUtil.EMPTY)
                .set(Outbound::getStatus, OutboundStatusEnum.CANCEL_OUT.getValue())
                .set(Outbound::getRemarks, deliveryCode));
        //解除出库明细和 订单明细、订单分配 的联系
        iOutboundItemService.update(new LambdaUpdateWrapper<OutboundItem>().eq(OutboundItem::getOutboundCode, outboundCode)
                .eq(OutboundItem::getGroupCode,groupCode)
                .set(OutboundItem::getDeliveryItemCode, StrUtil.EMPTY)
                .set(OutboundItem::getDeliveryDistributionCode, StrUtil.EMPTY)
                .set(OutboundItem::getStatus, OutboundStatusEnum.CANCEL_OUT.getValue()));
        //解除出库确认单和 订单明细、订单分配 的联系
        iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>().eq(OutboundRegister::getOutboundCode, outboundCode)
                .eq(OutboundRegister::getGroupCode,groupCode)
                .set(OutboundRegister::getDeliveryItemCode, StrUtil.EMPTY)
                .set(OutboundRegister::getDeliveryDistributionCode, StrUtil.EMPTY)
                .set(OutboundRegister::getStatus, OutboundStatusEnum.CANCEL_OUT.getValue()));

    }

    /**
     * 查询预打包单复核信息
     *
     * @param wavePickingCode 拣货单号
     * @author rfwang
     * @date 2021年7月10日
     */
    @Override
    public OutboundVO getPrepackagedOutboundInfo(String wavePickingCode, String orderTag) {
        OutboundVO outboundVO = new OutboundVO();
        List<OutboundItemVO> outboundItemVOArrayList = Lists.newArrayList();
        //1.设置预打包拣货库位，默认YDKW
        outboundVO.setLocationSerial(CommonConst.YDKW);
        Outbound outbound;
        if (OrderTagEnum.QUANTITY.getValue().equals(orderTag)) {
            //注：此时wavePickingCode就是出库单号
            outbound = this.getOne(new LambdaQueryWrapper<Outbound>().eq(Outbound::getCode, wavePickingCode)
                    .eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
            wavePickingCode = outbound.getWavePickingCode();
        } else {
            outbound = this.list(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                    .eq(Outbound::getGroupCode,UserUtil.getBranchCode())).get(NUM_ZERO);
        }

        BeanUtil.copyProperties(outbound, outboundVO);
        List<OutboundItem> outboundItems = iOutboundItemService.list(new LambdaQueryWrapper<OutboundItem>()
                .select(OutboundItem::getGoodCode,
                        OutboundItem::getGoodsName,
                        OutboundItem::getGoodsUnitName)
                .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundItem::getOutboundCode, outbound.getCode()));
        List<Delivery> deliveryList = iDeliveryService.list(new LambdaQueryWrapper<Delivery>()
                .select(Delivery::getCode)
                .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                .eq(Delivery::getWavePickingCode, wavePickingCode)
                .apply("pick_num > 0"));

        Map<String, Object> deliveryItemMap = iDeliveryItemService.list(new LambdaQueryWrapper<DeliveryItem>()
                .select(DeliveryItem::getGoodCode,
                        DeliveryItem::getDistributionQuantity)
                .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                .eq(DeliveryItem::getDeliveryCode, deliveryList.get(NUM_ZERO).getCode())).stream()
                .collect(Collectors.toMap(e -> e.getGoodCode(), e -> e.getDistributionQuantity()));
        //待复核包裹数
        Integer planPackQuantity = deliveryList.size();
        outboundItemVOArrayList = outboundItems.stream().map(info -> {
            OutboundItemVO itemVO = new OutboundItemVO();
            BeanUtil.copyProperties(info, itemVO);
            itemVO.setPlanQuantity(MapUtil.get(deliveryItemMap, itemVO.getGoodCode(), BigDecimal.class));
            return itemVO;
        }).collect(Collectors.toList());
        outboundVO.setOutboundItemVOList(outboundItemVOArrayList);
        outboundVO.setPlanPackQuantity(planPackQuantity);
        outboundVO.setWavePickingCode(wavePickingCode);
        return outboundVO;
    }

    /**
     * pda预打包确认
     *
     * @param wavePickingCode 波次号
     * @author rfwang
     * @date 2021年7月12日
     */
    @Override
    public PdaSwitchPageVO pdaPrepackagedRecheckCheck(String wavePickingCode) {
        PdaSwitchPageVO pdaSwitchPageVO = new PdaSwitchPageVO();
        //不缺货的情况下，走该方法
        //1.更新复核主单、复核明细、复核确认表的复核数量和状态
        Picking picking = pickingService.getOne(new LambdaQueryWrapper<Picking>()
                .select(Picking::getCode,
                        Picking::getPickingQuantity)
                .eq(Picking::getGroupCode,UserUtil.getBranchCode())
                .eq(Picking::getWavePickingCode, wavePickingCode));
        //总复核数量为拣货数量
        BigDecimal recheckQuantity = picking.getPickingQuantity();
        long recheckCount = this.list(new LambdaQueryWrapper<Outbound>()
                .select(Outbound::getCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .eq(Outbound::getWavePickingCode, wavePickingCode)).size();
        if (recheckCount == 0) {
            throw new ServiceException("复核单不存在，请确认。");
        }
        // “待复核”、“复核中”状态可以复核确认，其他状态不允许确认
        List<String> outboundStatusList = new ArrayList<String>(Arrays.asList(OutboundStatusEnum.NOT_OUT.getValue(),
                OutboundStatusEnum.PAET_OUT.getValue(), OutboundStatusEnum.FINISH_OUT.getValue()));
        long outboundCount = this.list(new LambdaQueryWrapper<Outbound>()
                .select(Outbound::getCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .eq(Outbound::getWavePickingCode, wavePickingCode).in(Outbound::getStatus, outboundStatusList)).size();
        if (outboundCount > 0) {
            throw new ServiceException("复核单已经复核完成，请确认。");
        }
        updateOutbound(wavePickingCode);
        //2.更新订单主单、订单明细、订单分配表的复核数量和状态
        updateDelivery(wavePickingCode);

        //3.更新任务信息
        // 更新任务已完成作业量，作业状态在占用作业时就已更新，所以不需要再更新
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,UserUtil.getBranchCode())
                .setSql(" job_quantity = job_quantity + " + recheckQuantity)
                .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));
        //复核过程中，不抛出出库确认的问题
        try {
            //4.出库确认
            log.info("波次复核：自动出库");
            outCheckService.autoOutConfirmation(wavePickingCode, StrUtil.EMPTY, NUM_ONE, NUM_ONE,UserUtil.getBranchCode());
        } catch (Exception e) {
            log.error("错误信息为：" + e.getMessage());
        }
        //5.调整pda跳转页面
        // 单据完成，可跳转到复核列表页面
        pdaSwitchPageVO.setToPage(NUM_THREE);
        return pdaSwitchPageVO;
    }

    @Transactional(rollbackFor = {Exception.class})
    public void updateOutbound(String wavePickingCode) {
        //更新拣货主单信息
        this.update(new LambdaUpdateWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .setSql("recheck_quantity = plan_quantity")
                .set(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        //更新拣货明细信息
        iOutboundItemService.update(new LambdaUpdateWrapper<OutboundItem>()
                .eq(OutboundItem::getWavePickingCode, wavePickingCode)
                .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                .setSql("recheck_quantity = plan_quantity")
                .set(OutboundItem::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        //更新拣货确认单信息
        iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getWavePickingCode, wavePickingCode)
                .setSql("recheck_quantity = plan_quantity")
                .set(OutboundRegister::getNotRecheckQuantity, BigDecimal.ZERO)
                .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
    }

    @Transactional(rollbackFor = {Exception.class})
    public void updateDelivery(String wavePickingCode) {
        //更新订单主单信息
        iDeliveryService.update(new LambdaUpdateWrapper<Delivery>()
                .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                .eq(Delivery::getWavePickingCode, wavePickingCode)
                .setSql("recheck_quantity = pick_num")
                .set(Delivery::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        //更新订单明细的拣货数量
        iDeliveryItemService.update(new LambdaUpdateWrapper<DeliveryItem>()
                .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                .eq(DeliveryItem::getWavePickingCode, wavePickingCode)
                .setSql("recheck_quantity = picking_quantity")
                .set(DeliveryItem::getNotRecheckQuantity, BigDecimal.ZERO)
                .set(DeliveryItem::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        //更新订单分配的拣货数量
        iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>()
                .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                .eq(DeliveryDistribution::getWavePickingCode, wavePickingCode)
                .setSql("recheck_quantity = picking_quantity")
                .set(DeliveryDistribution::getNotRecheckQuantity, BigDecimal.ZERO)
                .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
    }

    /**
     * pda预打包缺货登记
     *
     * @param wavePickingCode 波次号
     * @param deliveryDTOList 订单列表
     * @author rfwang
     * @date 2021年7月9日
     */
    @Override
    public PdaSwitchPageVO prepackagedStockout(String wavePickingCode, List<DeliveryDTO> deliveryDTOList, Integer planPickQuantity, Integer pickQuantity) {
        PdaSwitchPageVO pdaSwitchPageVO = new PdaSwitchPageVO();
        //缺货的情况下，走该方法
        //1.更新出库主单、出库明细、出库确认表的复核数量和状态
        int recheckCount = this.list(new LambdaQueryWrapper<Outbound>()
                .select(Outbound::getCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .eq(Outbound::getWavePickingCode, wavePickingCode)).size();
        if (recheckCount == 0) {
            throw new ServiceException("复核单不存在，请确认。");
        }
        // “待复核”、“复核中”状态可以复核确认，其他状态不允许确认
        List<String> outboundStatusList = new ArrayList<String>(Arrays.asList(OutboundStatusEnum.NOT_OUT.getValue(),
                OutboundStatusEnum.PAET_OUT.getValue(), OutboundStatusEnum.FINISH_OUT.getValue()));
        int outboundCount = this.list(new LambdaQueryWrapper<Outbound>()
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .select(Outbound::getCode)
                .eq(Outbound::getWavePickingCode, wavePickingCode).in(Outbound::getStatus, outboundStatusList)).size();
        if (outboundCount > 0) {
            throw new ServiceException("复核单已经复核完成，请确认。");
        }
        //复核完成的订单
        List<String> deliveryCodes = deliveryDTOList.stream().filter(e -> e.getRecheckQuantity().compareTo(BigDecimal.ZERO) > 0)
                .map(DeliveryDTO::getCode).collect(Collectors.toList());
        updateStockoutOutbound(deliveryCodes, wavePickingCode);
        //2.更新订单主单、订单明细、订单分配表的出库数量和状态
        updatePrepackagedStockoutDelivery(deliveryCodes, wavePickingCode);

        //3.更新任务信息
        // 更新任务已完成作业量，作业状态在占用作业时就已更新，所以不需要再更新
        Picking picking = pickingService.getOne(new LambdaQueryWrapper<Picking>()
                .eq(Picking::getGroupCode,UserUtil.getBranchCode())
                .select(Picking::getCode,
                        Picking::getPickingQuantity)
                .eq(Picking::getWavePickingCode, wavePickingCode));
        //任务数量= 总拣货数量*实际复核包裹数/计划复核包裹数
        BigDecimal jobQuantity = picking.getPickingQuantity().multiply(new BigDecimal(pickQuantity.toString()))
                .divide(new BigDecimal(planPickQuantity.toString()), 6, BigDecimal.ROUND_HALF_UP);
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,UserUtil.getBranchCode())
                .setSql(" job_quantity = job_quantity + " + jobQuantity)
                .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));

        //复核过程中，不抛出出库确认的问题
        try {
            //4.出库确认
            log.info("波次复核：自动出库");
            outCheckService.autoOutConfirmation(wavePickingCode, StrUtil.EMPTY, NUM_ZERO, NUM_ONE,UserUtil.getBranchCode());
        } catch (Exception e) {
            log.error("错误信息为：" + e.getMessage());
        }
        //5.调整pda跳转页面
        // 单据完成，可跳转到出库列表页面
        pdaSwitchPageVO.setToPage(NUM_THREE);
        return pdaSwitchPageVO;
    }

    @Transactional(rollbackFor = {Exception.class})
    public void updateStockoutOutbound(List<String> deliveryCodes, String wavePickingCode) {
        if (IterUtil.isEmpty(deliveryCodes)) {
            this.update(new LambdaUpdateWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                    .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                    .set(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
            //更新复核明细信息
            iOutboundItemService.update(new LambdaUpdateWrapper<OutboundItem>()
                    .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                    .eq(OutboundItem::getWavePickingCode, wavePickingCode)
                    .set(OutboundItem::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
            //更新复核确认单信息
            iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                    .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                    .eq(OutboundRegister::getWavePickingCode, wavePickingCode)
                    .set(OutboundRegister::getNotRecheckQuantity, BigDecimal.ZERO)
                    .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        } else {
            //更新复核主单信息
            this.update(new LambdaUpdateWrapper<Outbound>().in(Outbound::getSourceCode, deliveryCodes)
                    .eq(Outbound::getGroupCode, UserUtil.getBranchCode())
                    .setSql("recheck_quantity = plan_quantity")
                    .set(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
            //查询复核完成的出库单id
            List<String> outboundCodes = this.list(new LambdaQueryWrapper<Outbound>().select(Outbound::getCode)
                    .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                    .in(Outbound::getDeliveryCode, deliveryCodes)).stream().map(e -> e.getCode()).collect(Collectors.toList());
            //更新复核明细信息
            iOutboundItemService.update(new LambdaUpdateWrapper<OutboundItem>()
                    .eq(OutboundItem::getGroupCode,UserUtil.getBranchCode())
                    .in(OutboundItem::getOutboundCode, outboundCodes)
                    .setSql("recheck_quantity = plan_quantity")
                    .set(OutboundItem::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
            //更新复核确认单信息
            iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                    .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                    .in(OutboundRegister::getOutboundCode, outboundCodes)
                    .setSql("recheck_quantity = plan_quantity")
                    .set(OutboundRegister::getNotRecheckQuantity, BigDecimal.ZERO)
                    .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        }

    }

    @Transactional(rollbackFor = {Exception.class})
    public void updatePrepackagedStockoutDelivery(List<String> deliveryCodes, String wavePickingCode) {
        //完全缺货
        if (IterUtil.isEmpty(deliveryCodes)) {
            iDeliveryService.update(new UpdateWrapper<Delivery>().lambda()
                    .eq(Delivery::getGroupCode,UserUtil.getBranchCode()).eq(Delivery::getWavePickingCode, wavePickingCode).set(Delivery::getPickNum, BigDecimal.ZERO).set(Delivery::getWavePickingCode, StrUtil.EMPTY).set(Delivery::getPickStockOutNum, BigDecimal.ZERO).set(Delivery::getStatus, DeliveryStatusEnum.UN_DISTRIBUTION.getValue()).set(Delivery::getRecheckQuantity, BigDecimal.ZERO).set(Delivery::getRecheckRefundQuantity, BigDecimal.ZERO).set(Delivery::getDistributionNum, BigDecimal.ZERO));
            iDeliveryItemService.update(new UpdateWrapper<DeliveryItem>().lambda()
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                    .eq(DeliveryItem::getWavePickingCode, wavePickingCode).set(DeliveryItem::getPickingQuantity, BigDecimal.ZERO).set(DeliveryItem::getPickStockOutNum, BigDecimal.ZERO).set(DeliveryItem::getWavePickingCode, StrUtil.EMPTY).set(DeliveryItem::getDistributionQuantity, BigDecimal.ZERO).set(DeliveryItem::getStatus, DeliveryStatusEnum.ADD.getValue()).set(DeliveryItem::getRecheckQuantity, BigDecimal.ZERO).set(DeliveryItem::getDistributionQuantity, BigDecimal.ZERO));
            iDeliveryDistributionService.remove(new QueryWrapper<DeliveryDistribution>().lambda()
                    .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                    .eq(DeliveryDistribution::getWavePickingCode, wavePickingCode));
        } else {
            //更新订单主单信息
            iDeliveryService.update(new LambdaUpdateWrapper<Delivery>()
                    .eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                    .in(Delivery::getCode, deliveryCodes)
                    .setSql("recheck_quantity = pick_num")
                    .set(Delivery::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
            //更新订单明细的拣货数量
            iDeliveryItemService.update(new LambdaUpdateWrapper<DeliveryItem>()
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                    .in(DeliveryItem::getDeliveryCode, deliveryCodes)
                    .setSql("recheck_quantity = picking_quantity")
                    .set(DeliveryItem::getNotRecheckQuantity, BigDecimal.ZERO)
                    .set(DeliveryItem::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
            //更新订单分配的拣货数量
            iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>()
                    .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                    .in(DeliveryDistribution::getDeliveryCode, deliveryCodes)
                    .setSql("recheck_quantity = picking_quantity")
                    .set(DeliveryDistribution::getNotRecheckQuantity, BigDecimal.ZERO)
                    .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        }


    }

    /**
     * @param dto:
     * @description PDA端爆品复核
     * @author rfwang
     * @date 2021年7月15日
     **/
    @Override
    public PdaSwitchPageVO hotReviewCheck(OutboundRegisterDTO dto) {
        PdaSwitchPageVO switchPageVO = new PdaSwitchPageVO();
        //1.数据校验
        String wavePickingCode = dto.getWavePickingCode();
        String inventoryCode = dto.getInventoryCode();
        String goodCode = dto.getGoodCode();
        BigDecimal recheckQuantity = dto.getRecheckQuantity();
        switchPageVO.setToPage(NUM_ONE);
        switchPageVO.setMsg("操作成功");
        //根据波次号，获取oldInventoryCode
        List<OutboundRegister> outboundRegisterList = iOutboundRegisterService.list(new LambdaQueryWrapper<OutboundRegister>()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getWavePickingCode, wavePickingCode)
                .eq(OutboundRegister::getInventoryCode, inventoryCode));
        if (IterUtil.isEmpty(outboundRegisterList)) {
            throw new ServiceException("当前波次号、库存下没有出库登记信息");
        }
        String oldInventoryCode = outboundRegisterList.get(NUM_ZERO).getOldInventoryCode();
        validateHotReckeckInfo(wavePickingCode, oldInventoryCode, recheckQuantity);
        //2.处理作业任务
        // 更新任务状态, 此时也会自动占有任务
        iWmsTaskService.updateTaskWorkingBySourceCode(wavePickingCode);
        //3.回写订单相关的复核数量和状态
        updateDeliveryInfo(wavePickingCode, goodCode, oldInventoryCode);

        // 更新任务作业量，作业状态在占用作业时就已更新，所以不需要再更新
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,UserUtil.getBranchCode())
                .setSql(" job_quantity = job_quantity + " + recheckQuantity));
        //4.回写出库单相关复核数量和状态并判断能够自动出库
        updateOutboundInfo(wavePickingCode, goodCode, oldInventoryCode);

        //5.全部复核完后，符合自动出库条件的自动出库
        //自动出库
        long total = this.count(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        long totalRecheched = this.count(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .eq(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        if (total == totalRecheched) {

            //更新任务状态
            iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                    .eq(WmsTask::getGroupCode,UserUtil.getBranchCode())
                    .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));

            //复核过程中，不抛出出库确认的问题
            try {
                log.info("自动出库");
                outCheckService.autoOutConfirmation(wavePickingCode, StrUtil.EMPTY, NUM_ONE, NUM_ONE,UserUtil.getBranchCode());

                log.info("异步释放容器");
                outCheckService.releaseContainer(wavePickingCode,UserUtil.getBranchCode());
            } catch (Exception e) {
                log.error("错误信息为：" + e.getMessage());
            }

            switchPageVO.setToPage(NUM_TWO);
        }

        return switchPageVO;
    }

    private void validateHotReckeckInfo(String wavePickingCode, String oldInventoryCode, BigDecimal recheckQuantity) {
        List<OutboundRegister> outboundRegisterList = iOutboundRegisterService.list(new LambdaQueryWrapper<OutboundRegister>()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getWavePickingCode, wavePickingCode).eq(OutboundRegister::getOldInventoryCode, oldInventoryCode));
        if (IterUtil.isEmpty(outboundRegisterList)) {
            throw new ServiceException("复核商品数据不存在");
        }
        // 校验复核数量，必须>0
        if (ObjectUtil.isNull(recheckQuantity) || BigDecimal.ZERO.compareTo(recheckQuantity) >= 0) {
            throw new ServiceException("复核数量必须大于0");
        }
        WmsTask task = iWmsTaskService.getOne(new QueryWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,UserUtil.getBranchCode()));
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("任务信息不存在");
        }
        // 检查主单状态
        List<Outbound> outboundList = this.list(new QueryWrapper<Outbound>().lambda()
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode()).eq(Outbound::getWavePickingCode, wavePickingCode));
        if (IterUtil.isEmpty(outboundList)) {
            throw new ServiceException(TipConst.OUTBOUND_NOT_EXIST);
        }
    }

    @Transactional(rollbackFor = {Exception.class})
    public void updateDeliveryInfo(String wavePickingCode, String goodCode, String oldInventoryCode) {
        iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>()
                .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                .eq(DeliveryDistribution::getWavePickingCode, wavePickingCode)
                .eq(DeliveryDistribution::getOldInventoryCode, oldInventoryCode)
                .setSql("recheck_quantity = picking_quantity")
                .set(DeliveryDistribution::getNotRecheckQuantity, BigDecimal.ZERO)
                .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        iDeliveryItemService.updateRecheckQuantityAndStatsuByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
        deliveryService.updateRecheckQuantityByWavePickingCode(wavePickingCode);
    }

    @Transactional(rollbackFor = {Exception.class})
    public void updateOutboundInfo(String wavePickingCode, String goodCode, String oldInventoryCode) {
        iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getWavePickingCode, wavePickingCode)
                .eq(OutboundRegister::getOldInventoryCode, oldInventoryCode)
                .setSql("recheck_quantity = plan_quantity")
                .set(OutboundRegister::getNotRecheckQuantity, BigDecimal.ZERO)
                .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        iOutboundItemService.updateRecheckQuantityByWavePickingCode(wavePickingCode);
        this.updateRecheckQuantityByWavePickingCode(wavePickingCode);

    }

    /**
     * 更新出库单的复核数量和状态
     *
     * @param wavePickingCode 波次号
     * @author rfwang
     * @date 2021年7月16日
     */
    @Override
    public int updateRecheckQuantityByWavePickingCode(String wavePickingCode) {
        return this.baseMapper.updateRecheckQuantityByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
    }

    /**
     * @param entitys         分配明细:
     * @param wavePickingCode 波次号:
     * @param inventoryCode   库存号:
     * @description PDA端爆品缺货复核
     * @author rfwang
     * @date 2021年7月16日
     **/
    @Override
    public PdaSwitchPageVO hotRecheckStockout(List<DeliveryDistribution> entitys, String wavePickingCode, String inventoryCode) {
        PdaSwitchPageVO pdaSwitchPageVO = new PdaSwitchPageVO();
        pdaSwitchPageVO.setToPage(NUM_ONE);
        pdaSwitchPageVO.setMsg("操作成功");
        //缺货的情况下，走该方法
        //1.更新出库主单、出库明细、出库确认表的复核数量和状态
        int recheckCount = this.list(new LambdaQueryWrapper<Outbound>()
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .select(Outbound::getCode)
                .eq(Outbound::getWavePickingCode, wavePickingCode)).size();
        if (recheckCount == 0) {
            throw new ServiceException("复核单不存在，请确认。");
        }
        //根据波次号，获取oldInventoryCode
        List<OutboundRegister> outboundRegisterList = iOutboundRegisterService.list(new LambdaQueryWrapper<OutboundRegister>()
                .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(OutboundRegister::getWavePickingCode, wavePickingCode).eq(OutboundRegister::getInventoryCode, inventoryCode));
        if (IterUtil.isEmpty(outboundRegisterList)) {
            throw new ServiceException("当前波次号、库存下没有出库登记信息");
        }
        String oldInventoryCode = outboundRegisterList.get(NUM_ZERO).getOldInventoryCode();
        String outboundCode = outboundRegisterList.get(NUM_ZERO).getOutboundCode();
        BigDecimal recheckQuantity = entitys.stream().map(info -> info.getRecheckQuantity()).reduce(BigDecimal.ZERO, BigDecimal::add);
        updateHotStockoutOutbound(entitys, wavePickingCode, oldInventoryCode);
        // 更新任务状态, 此时也会自动占有任务
        iWmsTaskService.updateTaskWorkingBySourceCode(wavePickingCode);
        //2.更新订单主单、订单明细、订单分配表的出库数量和状态
        updateHotStockoutDelivery(entitys, wavePickingCode, oldInventoryCode);
        //缺货登记返还库存
        updateInventory(inventoryCode,entitys,outboundCode);

        // 3.更新任务作业量，作业状态在占用作业时就已更新，所以不需要再更新
        iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                .eq(WmsTask::getGroupCode,UserUtil.getBranchCode())
                .setSql(" job_quantity = job_quantity + " + recheckQuantity));

        //4.全部复核完后，符合自动出库条件的自动出库
        //自动出库
        long total = this.count(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode).eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
        long totalRecheched = this.count(new LambdaQueryWrapper<Outbound>().eq(Outbound::getWavePickingCode, wavePickingCode)
                .eq(Outbound::getGroupCode,UserUtil.getBranchCode())
                .eq(Outbound::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        if (total == totalRecheched) {

            //更新任务状态
            iWmsTaskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, wavePickingCode)
                    .eq(WmsTask::getGroupCode,UserUtil.getBranchCode())
                    .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED));
            //复核过程中，不抛出出库确认的问题
            try {
                log.info("自动出库");
                outCheckService.autoOutConfirmation(wavePickingCode, StrUtil.EMPTY, NUM_ONE, NUM_ONE,UserUtil.getBranchCode());

                log.info("异步释放容器");
                outCheckService.releaseContainer(wavePickingCode,UserUtil.getBranchCode());
            } catch (Exception e) {
                log.error("错误信息为：" + e.getMessage());
            }

            pdaSwitchPageVO.setToPage(NUM_TWO);
        }
        return pdaSwitchPageVO;
    }

    public void updateHotStockoutOutbound(List<DeliveryDistribution> entitys, String wavePickingCode, String oldInventoryCode) {
        //不缺货集合
        List<String> distributionCodesWithoutStockout = entitys.stream().filter(e -> e.getRecheckQuantity().compareTo(e.getPickingQuantity()) == NUM_ZERO)
                .map(info -> info.getCode()).collect(Collectors.toList());
        //缺货集合
        List<DeliveryDistribution> distributionCodesPartStockout = entitys.stream().filter(e -> e.getRecheckQuantity()
                .compareTo(e.getPickingQuantity()) < NUM_ZERO).collect(Collectors.toList());
        if (IterUtil.isNotEmpty(distributionCodesWithoutStockout)) {
            iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                    .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                    .eq(OutboundRegister::getOldInventoryCode, oldInventoryCode)
                    .in(OutboundRegister::getDeliveryDistributionCode, distributionCodesWithoutStockout)
                    .setSql("recheck_quantity = plan_quantity")
                    .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
        }
        if (IterUtil.isNotEmpty(distributionCodesPartStockout)) {
            distributionCodesPartStockout.stream().forEach(info -> {
                BigDecimal recheckQuantity = info.getRecheckQuantity();
                BigDecimal recheckRefundQuantity = info.getPickingQuantity().subtract(info.getRecheckQuantity());
                iOutboundRegisterService.update(new LambdaUpdateWrapper<OutboundRegister>()
                        .eq(OutboundRegister::getGroupCode,UserUtil.getBranchCode())
                        .eq(OutboundRegister::getOldInventoryCode, oldInventoryCode)
                        .eq(OutboundRegister::getDeliveryDistributionCode, info.getCode())
                        .set(OutboundRegister::getRecheckQuantity, recheckQuantity)
                        .set(OutboundRegister::getRecheckRefundQuantity, recheckRefundQuantity)
                        .set(OutboundRegister::getNotRecheckQuantity, BigDecimal.ZERO)
                        .set(OutboundRegister::getStatus, OutboundStatusEnum.NOT_OUT.getValue()));
            });
        }
        iOutboundItemService.updateRecheckQuantityByWavePickingCode(wavePickingCode);
        this.updateRecheckQuantityByWavePickingCode(wavePickingCode);
    }

    private void updateInventory(String inventoryCode,List<DeliveryDistribution> entitys,String outboundCode) {
        //缺货集合
        List<DeliveryDistribution> distributionCodesPartStockout = entitys.stream().filter(e -> e.getRecheckQuantity()
                .compareTo(e.getPickingQuantity()) < NUM_ZERO).collect(Collectors.toList());
        distributionCodesPartStockout.stream().forEach(info -> {
            BigDecimal recheckRefundQuantity = info.getPickingQuantity().subtract(info.getRecheckQuantity());
            iInventoryService.stockoutInventory(inventoryCode, recheckRefundQuantity, StockoutTypeEnum.OUT_CHECK.getValue(), outboundCode);
        });


    }

    private void updateHotStockoutDelivery(List<DeliveryDistribution> entitys, String wavePickingCode, String oldInventoryCode) {
        //不缺货集合
        List<String> distributionCodesWithoutStockout = entitys.stream().filter(e -> e.getRecheckQuantity().compareTo(e.getPickingQuantity()) == NUM_ZERO)
                .map(info -> info.getCode()).collect(Collectors.toList());
        //缺货集合
        List<DeliveryDistribution> distributionCodesPartStockout = entitys.stream().filter(e -> e.getRecheckQuantity()
                .compareTo(e.getPickingQuantity()) < NUM_ZERO).collect(Collectors.toList());
        if (IterUtil.isNotEmpty(distributionCodesWithoutStockout)) {
            iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>()
                    .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                    .in(DeliveryDistribution::getCode, distributionCodesWithoutStockout)
                    .eq(DeliveryDistribution::getOldInventoryCode, oldInventoryCode)
                    .setSql("recheck_quantity = picking_quantity")
                    .set(DeliveryDistribution::getNotRecheckQuantity, BigDecimal.ZERO)
                    .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        }
        if (IterUtil.isNotEmpty(distributionCodesPartStockout)) {
            distributionCodesPartStockout.stream().forEach(info -> {
                BigDecimal recheckQuantity = info.getRecheckQuantity();
                BigDecimal recheckRefundQuantity = info.getPickingQuantity().subtract(info.getRecheckQuantity());
                iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>()
                        .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                        .eq(DeliveryDistribution::getCode, info.getCode())
                        .eq(DeliveryDistribution::getOldInventoryCode, oldInventoryCode)
                        .set(DeliveryDistribution::getRecheckQuantity, recheckQuantity)
                        .set(DeliveryDistribution::getNotRecheckQuantity, BigDecimal.ZERO)
                        .set(DeliveryDistribution::getRecheckRefundQuantity, recheckRefundQuantity)
                        .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
            });
        }
        iDeliveryItemService.updateRecheckQuantityAndStatsuByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
        deliveryService.updateRecheckQuantityAndStatsuByWavePickingCode(wavePickingCode);
    }

    /**
     * 根据订单号获取出库单号
     * @author rfwang
     * @date 2022/03/11
     * @param deliveryCodes
     * @param groupCode
     */
    @Override
    public List<String> getOutboundCodesByDeliveryCodes(Set<String> deliveryCodes,String groupCode){
        List<String> outboundCodes = this.list(new LambdaQueryWrapper<Outbound>()
                .select(Outbound::getCode)
                .in(Outbound::getDeliveryCode, deliveryCodes)
                .eq(Outbound::getGroupCode, groupCode)).stream()
                .map(Outbound::getCode)
                .collect(Collectors.toList());
        return outboundCodes;
    }

    /**
     * @description
     * PDA端按面单复核
     * @author rfwang
     * @date 2022/03/16 10:10
     * @param deliveryItemDTO
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public PdaSwitchPageVO recheckByWaybillCode(DeliveryItemDTO deliveryItemDTO){
        String groupCode = deliveryItemDTO.getGroupCode();
        String deliveryCode = deliveryItemDTO.getDeliveryCode();
        String code = deliveryItemDTO.getCode();
        BigDecimal recheckQuantity = deliveryItemDTO.getRecheckQuantity();
        PdaSwitchPageVO switchPageVO = new PdaSwitchPageVO();
        // 默认PDA不跳转
        switchPageVO.setToPage(NumConst.NUM_ZERO);
        switchPageVO.setMsg("操作成功");

        // 校验复核数量，必须>0
        if (ObjectUtil.isNull(recheckQuantity) || BigDecimal.ZERO.compareTo(recheckQuantity) >= 0) {
            throw new ServiceException("复核数量必须大于0");
        }
        //检查主单状态
        Delivery delivery = iDeliveryService.getOne(new LambdaQueryWrapper<Delivery>().eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode,groupCode));
        if (ObjectUtil.isNull(delivery)) {
            throw new ServiceException("订单不存在");
        }
        if (!StrUtil.equalsAny(delivery.getStatus(),DeliveryStatusEnum.UN_RECHECK.getValue(),DeliveryStatusEnum.RECHECKING.getValue())) {
            throw new ServiceException("订单非待复核、复核中状态，无法复核确认");
        }
        Outbound outbound = this.getOne(new LambdaQueryWrapper<Outbound>().eq(Outbound::getDeliveryCode, delivery.getCode())
                .eq(Outbound::getGroupCode,groupCode));
        if (null == outbound) {
            throw new ServiceException("待复核的出库单不能为空");
        }
        String wavePickingCode = outbound.getWavePickingCode();

        // 计算出库单明细数量是否超出
        DeliveryItem deliveryItem = iDeliveryItemService.getOne(new LambdaQueryWrapper<DeliveryItem>()
                .eq(DeliveryItem::getCode, code));
        BigDecimal diffQuantity = deliveryItem.getPickingQuantity().subtract(deliveryItem.getRecheckRefundQuantity()).subtract(deliveryItem.getRecheckQuantity());
        if (diffQuantity.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("订单复核数量与缺货数量之和超过了该明细的总复核数量，无法进行复核确认");
        }
        if (diffQuantity.compareTo(recheckQuantity) < 0) {
            throw new ServiceException("商品的复核数量不能超过待复核数量");
        }

        //更新复核单、复核明细、复核登记的复核数量、待复核数量和状态
        updateQuantityAndStatus(outbound.getCode(), code, recheckQuantity,groupCode);

        //更新订单复核数量及状态
        iDeliveryService.updateDeliveryRecheckQuantity(deliveryItemDTO);


        int finishItemFlag = iDeliveryItemService.count(new LambdaQueryWrapper<DeliveryItem>().eq(DeliveryItem::getCode, code).eq(DeliveryItem::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        if (finishItemFlag > 0) {
            // 明细复核完成，则可跳转到复核详情页面
            switchPageVO.setToPage(NUM_ONE);

            //获取主单完成的数量
            int finishDeliveryFlag = iDeliveryService.count(new LambdaQueryWrapper<Delivery>().eq(Delivery::getCode, deliveryCode).eq(Delivery::getGroupCode, groupCode).eq(Delivery::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));

            if (finishDeliveryFlag > 0) {
                switchPageVO.setToPage(NumConst.NUM_TWO);

                //由于前面更新出库单复核数量的时候更新了状态，会导致出库单再次更新出现失败的情况，所以直接查询出库单状态即可
                Outbound outboundNew = this.getOne(new LambdaQueryWrapper<Outbound>().eq(Outbound::getCode, outbound.getCode()).eq(Outbound::getGroupCode,groupCode));
                //更新任务状态
                iWmsTaskService.updateTaskStatusByWaybillCheck(wavePickingCode,groupCode,recheckQuantity,false);

                if (OutboundStatusEnum.NOT_OUT.getValue().equals(outboundNew.getStatus())) {

                    //复核过程中，不抛出出库确认的问题
                    try {
                        //自动出库
                        log.info("自动出库");
                        outCheckService.autoOutConfirmation(StrUtil.EMPTY, deliveryCode, NUM_ONE, NUM_ONE,groupCode);

                    } catch (Exception e) {
                        log.error("错误信息为：" + e.getMessage());
                    }
                }
            } else {
                //更新任务复核数量
                iWmsTaskService.updateTaskStatusByWaybillCheck(wavePickingCode,groupCode,recheckQuantity,true);
            }

        } else {
            //更新任务复核数量
            iWmsTaskService.updateTaskStatusByWaybillCheck(wavePickingCode,groupCode,recheckQuantity,true);
        }
        return switchPageVO;

    }

    /**
     * 根据出库单的计划数量和（复核数量+复核缺货数量）关系更新出库单复核数量和状态
     * @author rfwang
     * @date 2022/3/16 13:13
     * @param code 出库单号
     *@param  recheckQuantity 复核数量
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateQuantityAndStatus(String code,String deliveryItemCode,  BigDecimal recheckQuantity,String groupCode){
        //更新出库登记的复核数量和状态
        iOutboundRegisterService.updateRecheckQuantityAndStatus(code, deliveryItemCode, recheckQuantity,groupCode);
        //更新出库明细的复核数量和状态
        iOutboundItemService.updateRecheckQuantity(code,groupCode);
        //更新出库单的复核数量和状态
        return this.baseMapper.updateRecheckQuantity(code,groupCode);
    }

}
